001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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 addDateSortable(String name, Date value) {
136                    if (value == null) {
137                            return;
138                    }
139    
140                    addDateSortable(name, new Date[] {value});
141            }
142    
143            @Override
144            public void addDateSortable(String name, Date[] values) {
145                    if (values == null) {
146                            return;
147                    }
148    
149                    String[] dates = new String[values.length];
150                    Long[] datesTime = new Long[values.length];
151    
152                    for (int i = 0; i < values.length; i++) {
153                            dates[i] = _dateFormat.format(values[i]);
154                            datesTime[i] = values[i].getTime();
155                    }
156    
157                    createSortableNumericField(name, true, datesTime);
158    
159                    addKeyword(name, dates);
160            }
161    
162            @Override
163            public void addFile(String name, byte[] bytes, String fileExt) {
164                    InputStream is = new UnsyncByteArrayInputStream(bytes);
165    
166                    addFile(name, is, fileExt);
167            }
168    
169            @Override
170            public void addFile(String name, File file, String fileExt)
171                    throws IOException {
172    
173                    InputStream is = new FileInputStream(file);
174    
175                    addFile(name, is, fileExt);
176            }
177    
178            @Override
179            public void addFile(String name, InputStream is, String fileExt) {
180                    addText(name, FileUtil.extractText(is, fileExt));
181            }
182    
183            @Override
184            public void addFile(
185                    String name, InputStream is, String fileExt, int maxStringLength) {
186    
187                    addText(name, FileUtil.extractText(is, fileExt, maxStringLength));
188            }
189    
190            @Override
191            public void addKeyword(String name, boolean value) {
192                    addKeyword(name, String.valueOf(value));
193            }
194    
195            @Override
196            public void addKeyword(String name, Boolean value) {
197                    addKeyword(name, String.valueOf(value));
198            }
199    
200            @Override
201            public void addKeyword(String name, boolean[] 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, Boolean[] 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, double value) {
220                    addKeyword(name, String.valueOf(value));
221            }
222    
223            @Override
224            public void addKeyword(String name, Double value) {
225                    addKeyword(name, String.valueOf(value));
226            }
227    
228            @Override
229            public void addKeyword(String name, double[] 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, Double[] 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, float value) {
248                    addKeyword(name, String.valueOf(value));
249            }
250    
251            @Override
252            public void addKeyword(String name, Float value) {
253                    addKeyword(name, String.valueOf(value));
254            }
255    
256            @Override
257            public void addKeyword(String name, float[] 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, Float[] 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, int value) {
276                    addKeyword(name, String.valueOf(value));
277            }
278    
279            @Override
280            public void addKeyword(String name, int[] values) {
281                    if (values == null) {
282                            return;
283                    }
284    
285                    addKeyword(name, ArrayUtil.toStringArray(values));
286            }
287    
288            @Override
289            public void addKeyword(String name, Integer value) {
290                    addKeyword(name, String.valueOf(value));
291            }
292    
293            @Override
294            public void addKeyword(String name, Integer[] 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, long value) {
304                    addKeyword(name, String.valueOf(value));
305            }
306    
307            @Override
308            public void addKeyword(String name, Long value) {
309                    addKeyword(name, String.valueOf(value));
310            }
311    
312            @Override
313            public void addKeyword(String name, long[] 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, Long[] 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, short value) {
332                    addKeyword(name, String.valueOf(value));
333            }
334    
335            @Override
336            public void addKeyword(String name, Short value) {
337                    addKeyword(name, String.valueOf(value));
338            }
339    
340            @Override
341            public void addKeyword(String name, short[] values) {
342                    if (values == null) {
343                            return;
344                    }
345    
346                    addKeyword(name, ArrayUtil.toStringArray(values));
347            }
348    
349            @Override
350            public void addKeyword(String name, Short[] values) {
351                    if (values == null) {
352                            return;
353                    }
354    
355                    addKeyword(name, ArrayUtil.toStringArray(values));
356            }
357    
358            @Override
359            public void addKeyword(String name, String value) {
360                    addKeyword(name, value, false);
361            }
362    
363            @Override
364            public void addKeyword(String name, String value, boolean lowerCase) {
365                    createKeywordField(name, value, lowerCase);
366    
367                    createSortableKeywordField(name, value);
368            }
369    
370            @Override
371            public void addKeyword(String name, String[] values) {
372                    if (values == null) {
373                            return;
374                    }
375    
376                    Field field = new Field(name, values);
377    
378                    _fields.put(name, field);
379            }
380    
381            @Override
382            public void addKeywordSortable(String name, Boolean value) {
383                    String valueString = String.valueOf(value);
384    
385                    createKeywordField(name, valueString, false);
386    
387                    createSortableTextField(name, valueString);
388            }
389    
390            @Override
391            public void addKeywordSortable(String name, Boolean[] values) {
392                    if (values == null) {
393                            return;
394                    }
395    
396                    String[] valuesString = ArrayUtil.toStringArray(values);
397    
398                    Field field = new Field(name, valuesString);
399    
400                    _fields.put(name, field);
401    
402                    createSortableTextField(name, valuesString);
403            }
404    
405            @Override
406            public void addKeywordSortable(String name, String value) {
407                    createKeywordField(name, value, false);
408    
409                    createSortableTextField(name, value);
410            }
411    
412            @Override
413            public void addKeywordSortable(String name, String[] values) {
414                    Field field = new Field(name, values);
415    
416                    _fields.put(name, field);
417    
418                    createSortableTextField(name, values);
419            }
420    
421            @Override
422            public void addLocalizedKeyword(String name, Map<Locale, String> values) {
423                    addLocalizedKeyword(name, values, false);
424            }
425    
426            @Override
427            public void addLocalizedKeyword(
428                    String name, Map<Locale, String> values, boolean lowerCase) {
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                    _fields.put(name, field);
451            }
452    
453            @Override
454            public void addLocalizedKeyword(
455                    String name, Map<Locale, String> values, boolean lowerCase,
456                    boolean sortable) {
457    
458                    if ((values == null) || values.isEmpty()) {
459                            return;
460                    }
461    
462                    if (lowerCase) {
463                            Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
464                                    values.size());
465    
466                            for (Map.Entry<Locale, String> entry : values.entrySet()) {
467                                    String value = GetterUtil.getString(entry.getValue());
468    
469                                    lowerCaseValues.put(
470                                            entry.getKey(), StringUtil.toLowerCase(value));
471                            }
472    
473                            values = lowerCaseValues;
474                    }
475    
476                    Field field = new Field(name, values);
477    
478                    field.setSortable(sortable);
479    
480                    _fields.put(name, field);
481            }
482    
483            @Override
484            public void addLocalizedText(String name, Map<Locale, String> values) {
485                    if ((values == null) || values.isEmpty()) {
486                            return;
487                    }
488    
489                    Field field = new Field(name, values);
490    
491                    field.setTokenized(true);
492    
493                    _fields.put(name, field);
494            }
495    
496            /**
497             * @deprecated As of 6.1.0
498             */
499            @Override
500            public void addModifiedDate() {
501                    addModifiedDate(new Date());
502            }
503    
504            /**
505             * @deprecated As of 6.1.0
506             */
507            @Override
508            public void addModifiedDate(Date modifiedDate) {
509                    addDate(Field.MODIFIED, modifiedDate);
510            }
511    
512            @Override
513            public void addNumber(String name, double value) {
514                    createNumberField(name, Double.valueOf(value));
515            }
516    
517            @Override
518            public void addNumber(String name, Double value) {
519                    createNumberField(name, value);
520            }
521    
522            @Override
523            public void addNumber(String name, double[] values) {
524                    if (values == null) {
525                            return;
526                    }
527    
528                    createNumberField(name, ArrayUtil.toArray(values));
529            }
530    
531            @Override
532            public void addNumber(String name, Double[] values) {
533                    createNumberField(name, values);
534            }
535    
536            @Override
537            public void addNumber(String name, float value) {
538                    createNumberField(name, Float.valueOf(value));
539            }
540    
541            @Override
542            public void addNumber(String name, Float value) {
543                    createNumberField(name, value);
544            }
545    
546            @Override
547            public void addNumber(String name, float[] values) {
548                    if (values == null) {
549                            return;
550                    }
551    
552                    createNumberField(name, ArrayUtil.toArray(values));
553            }
554    
555            @Override
556            public void addNumber(String name, Float[] values) {
557                    createNumberField(name, values);
558            }
559    
560            @Override
561            public void addNumber(String name, int value) {
562                    createNumberField(name, Integer.valueOf(value));
563            }
564    
565            @Override
566            public void addNumber(String name, int[] values) {
567                    if (values == null) {
568                            return;
569                    }
570    
571                    createNumberField(name, ArrayUtil.toArray(values));
572            }
573    
574            @Override
575            public void addNumber(String name, Integer value) {
576                    createNumberField(name, value);
577            }
578    
579            @Override
580            public void addNumber(String name, Integer[] values) {
581                    createNumberField(name, values);
582            }
583    
584            @Override
585            public void addNumber(String name, long value) {
586                    createNumberField(name, Long.valueOf(value));
587            }
588    
589            @Override
590            public void addNumber(String name, Long value) {
591                    createNumberField(name, value);
592            }
593    
594            @Override
595            public void addNumber(String name, long[] values) {
596                    if (values == null) {
597                            return;
598                    }
599    
600                    createNumberField(name, ArrayUtil.toArray(values));
601            }
602    
603            @Override
604            public void addNumber(String name, Long[] values) {
605                    createNumberField(name, values);
606            }
607    
608            @Override
609            public void addNumber(String name, String value) {
610                    createNumberField(name, Long.valueOf(value));
611            }
612    
613            /**
614             * @deprecated As of 7.0.0
615             */
616            @Deprecated
617            public void addNumber(
618                    String name, String value, Class<? extends Number> clazz) {
619    
620                    if (Validator.isNull(value)) {
621                            return;
622                    }
623    
624                    addNumber(name, new String[] {value}, clazz);
625            }
626    
627            @Override
628            public void addNumber(String name, String[] values) {
629                    if (values == null) {
630                            return;
631                    }
632    
633                    Long[] longs = new Long[values.length];
634    
635                    for (int i = 0; i < values.length; i++) {
636                            longs[i] = Long.valueOf(values[i]);
637                    }
638    
639                    createNumberField(name, longs);
640            }
641    
642            /**
643             * @deprecated As of 7.0.0
644             */
645            @Deprecated
646            public void addNumber(
647                    String name, String[] values, Class<? extends Number> clazz) {
648    
649                    if (values == null) {
650                            return;
651                    }
652    
653                    String sortableFieldName = getSortableFieldName(name);
654    
655                    Field field = new Field(sortableFieldName, values);
656    
657                    field.setNumeric(true);
658                    field.setNumericClass(clazz);
659    
660                    _fields.put(sortableFieldName, field);
661    
662                    addKeyword(name, values);
663            }
664    
665            @Override
666            public void addNumberSortable(String name, Double value) {
667                    createNumberFieldWithTypedSortable(name, value);
668            }
669    
670            @Override
671            public void addNumberSortable(String name, Double[] values) {
672                    createNumberFieldWithTypedSortable(name, values);
673            }
674    
675            @Override
676            public void addNumberSortable(String name, Float value) {
677                    createNumberFieldWithTypedSortable(name, value);
678            }
679    
680            @Override
681            public void addNumberSortable(String name, Float[] values) {
682                    createNumberFieldWithTypedSortable(name, values);
683            }
684    
685            @Override
686            public void addNumberSortable(String name, Integer value) {
687                    createNumberFieldWithTypedSortable(name, value);
688            }
689    
690            @Override
691            public void addNumberSortable(String name, Integer[] values) {
692                    createNumberFieldWithTypedSortable(name, values);
693            }
694    
695            @Override
696            public void addNumberSortable(String name, Long value) {
697                    createNumberFieldWithTypedSortable(name, value);
698            }
699    
700            @Override
701            public void addNumberSortable(String name, Long[] values) {
702                    createNumberFieldWithTypedSortable(name, values);
703            }
704    
705            @Override
706            public void addText(String name, String value) {
707                    if (Validator.isNull(value)) {
708                            return;
709                    }
710    
711                    Field field = new Field(name, value);
712    
713                    field.setTokenized(true);
714    
715                    _fields.put(name, field);
716    
717                    createSortableKeywordField(name, value);
718            }
719    
720            @Override
721            public void addText(String name, String[] values) {
722                    if (values == null) {
723                            return;
724                    }
725    
726                    Field field = new Field(name, values);
727    
728                    field.setTokenized(true);
729    
730                    _fields.put(name, field);
731    
732                    createSortableKeywordField(name, values);
733            }
734    
735            @Override
736            public void addTextSortable(String name, String value) {
737                    if (Validator.isNull(value)) {
738                            return;
739                    }
740    
741                    Field field = new Field(name, value);
742    
743                    field.setTokenized(true);
744    
745                    _fields.put(name, field);
746    
747                    createSortableTextField(name, value);
748            }
749    
750            @Override
751            public void addTextSortable(String name, String[] values) {
752                    if (values == null) {
753                            return;
754                    }
755    
756                    Field field = new Field(name, values);
757    
758                    field.setTokenized(true);
759    
760                    _fields.put(name, field);
761    
762                    createSortableTextField(name, values);
763            }
764    
765            @Override
766            public void addUID(String portletId, long field1) {
767                    addUID(portletId, String.valueOf(field1));
768            }
769    
770            @Override
771            public void addUID(String portletId, long field1, String field2) {
772                    addUID(portletId, String.valueOf(field1), field2);
773            }
774    
775            @Override
776            public void addUID(String portletId, Long field1) {
777                    addUID(portletId, field1.longValue());
778            }
779    
780            @Override
781            public void addUID(String portletId, Long field1, String field2) {
782                    addUID(portletId, field1.longValue(), field2);
783            }
784    
785            @Override
786            public void addUID(String portletId, String field1) {
787                    addUID(portletId, field1, null);
788            }
789    
790            @Override
791            public void addUID(String portletId, String field1, String field2) {
792                    addUID(portletId, field1, field2, null);
793            }
794    
795            @Override
796            public void addUID(
797                    String portletId, String field1, String field2, String field3) {
798    
799                    addUID(portletId, field1, field2, field3, null);
800            }
801    
802            @Override
803            public void addUID(
804                    String portletId, String field1, String field2, String field3,
805                    String field4) {
806    
807                    String uid = portletId + _UID_PORTLET + field1;
808    
809                    if (field2 != null) {
810                            uid += _UID_FIELD + field2;
811                    }
812    
813                    if (field3 != null) {
814                            uid += _UID_FIELD + field3;
815                    }
816    
817                    if (field4 != null) {
818                            uid += _UID_FIELD + field4;
819                    }
820    
821                    addKeyword(Field.UID, uid);
822            }
823    
824            @Override
825            public Object clone() {
826                    DocumentImpl documentImpl = new DocumentImpl();
827    
828                    documentImpl.setSortableTextFields(_sortableTextFields);
829    
830                    return documentImpl;
831            }
832    
833            @Override
834            public String get(Locale locale, String name) {
835                    if (locale == null) {
836                            return get(name);
837                    }
838    
839                    String localizedName = getLocalizedName(locale, name);
840    
841                    Field field = _fields.get(localizedName);
842    
843                    if (field == null) {
844                            field = _fields.get(name);
845                    }
846    
847                    if (field == null) {
848                            return StringPool.BLANK;
849                    }
850    
851                    return field.getValue();
852            }
853    
854            @Override
855            public String get(Locale locale, String name, String defaultName) {
856                    if (locale == null) {
857                            return get(name, defaultName);
858                    }
859    
860                    String localizedName = getLocalizedName(locale, name);
861    
862                    Field field = _fields.get(localizedName);
863    
864                    if (field == null) {
865                            localizedName = getLocalizedName(locale, defaultName);
866    
867                            field = _fields.get(localizedName);
868                    }
869    
870                    if (field == null) {
871                            return StringPool.BLANK;
872                    }
873    
874                    return field.getValue();
875            }
876    
877            @Override
878            public String get(String name) {
879                    Field field = _fields.get(name);
880    
881                    if (field == null) {
882                            return StringPool.BLANK;
883                    }
884    
885                    return field.getValue();
886            }
887    
888            @Override
889            public String get(String name, String defaultName) {
890                    Field field = _fields.get(name);
891    
892                    if (field == null) {
893                            return get(defaultName);
894                    }
895    
896                    return field.getValue();
897            }
898    
899            @Override
900            public Date getDate(String name) throws ParseException {
901                    DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
902                            _INDEX_DATE_FORMAT_PATTERN);
903    
904                    return dateFormat.parse(get(name));
905            }
906    
907            @Override
908            public Field getField(String name) {
909                    return _fields.get(name);
910            }
911    
912            @Override
913            public Map<String, Field> getFields() {
914                    return _fields;
915            }
916    
917            @Override
918            public String getPortletId() {
919                    String uid = getUID();
920    
921                    int pos = uid.indexOf(_UID_PORTLET);
922    
923                    return uid.substring(0, pos);
924            }
925    
926            @Override
927            public String getUID() {
928                    Field field = _fields.get(Field.UID);
929    
930                    if (field == null) {
931                            throw new RuntimeException("UID is not set");
932                    }
933    
934                    return field.getValue();
935            }
936    
937            @Override
938            public String[] getValues(String name) {
939                    Field field = _fields.get(name);
940    
941                    if (field == null) {
942                            return new String[] {StringPool.BLANK};
943                    }
944    
945                    return field.getValues();
946            }
947    
948            @Override
949            public boolean hasField(String name) {
950                    if (_fields.containsKey(name)) {
951                            return true;
952                    }
953    
954                    return false;
955            }
956    
957            @Override
958            public boolean isDocumentSortableTextField(String name) {
959                    return _sortableTextFields.contains(name);
960            }
961    
962            @Override
963            public void remove(String name) {
964                    _fields.remove(name);
965            }
966    
967            public void setFields(Map<String, Field> fields) {
968                    _fields = fields;
969            }
970    
971            @Override
972            public void setSortableTextFields(String[] sortableTextFields) {
973                    _sortableTextFields = SetUtil.fromArray(sortableTextFields);
974            }
975    
976            @Override
977            public String toString() {
978                    StringBundler sb = new StringBundler();
979    
980                    sb.append(StringPool.OPEN_CURLY_BRACE);
981    
982                    boolean firstField = true;
983    
984                    for (Field field : _fields.values()) {
985                            if (!firstField) {
986                                    sb.append(StringPool.COMMA);
987                                    sb.append(StringPool.SPACE);
988                            }
989                            else {
990                                    firstField = false;
991                            }
992    
993                            sb.append(field.getName());
994                            sb.append(StringPool.EQUAL);
995                            sb.append(Arrays.toString(field.getValues()));
996                    }
997    
998                    sb.append(StringPool.CLOSE_CURLY_BRACE);
999    
1000                    return sb.toString();
1001            }
1002    
1003            protected void createKeywordField(
1004                    String name, String value, boolean lowerCase) {
1005    
1006                    if (lowerCase && Validator.isNotNull(value)) {
1007                            value = StringUtil.toLowerCase(value);
1008                    }
1009    
1010                    Field field = new Field(name, value);
1011    
1012                    _fields.put(name, field);
1013    
1014                    for (String fieldName : Field.UNSCORED_FIELD_NAMES) {
1015                            if (StringUtil.equalsIgnoreCase(name, fieldName)) {
1016                                    field.setBoost(0);
1017                            }
1018                    }
1019            }
1020    
1021            protected void createNumberField(
1022                    String name, boolean typify, Number value) {
1023    
1024                    if (value == null) {
1025                            return;
1026                    }
1027    
1028                    String valueString = String.valueOf(value);
1029    
1030                    createSortableNumericField(name, typify, valueString, value.getClass());
1031    
1032                    Field field = new Field(name, valueString);
1033    
1034                    _fields.put(name, field);
1035            }
1036    
1037            protected <T extends Number & Comparable<? super T>> void createNumberField(
1038                    String name, boolean typify, T... values) {
1039    
1040                    if (values == null) {
1041                            return;
1042                    }
1043    
1044                    createSortableNumericField(name, typify, values);
1045    
1046                    Field field = new Field(name, ArrayUtil.toStringArray(values));
1047    
1048                    _fields.put(name, field);
1049            }
1050    
1051            protected void createNumberField(String name, Number value) {
1052                    createNumberField(name, false, value);
1053            }
1054    
1055            protected <T extends Number & Comparable<? super T>> void createNumberField(
1056                    String name, T... values) {
1057    
1058                    createNumberField(name, false, values);
1059            }
1060    
1061            protected void createNumberFieldWithTypedSortable(
1062                    String name, Number value) {
1063    
1064                    createNumberField(name, true, value);
1065            }
1066    
1067            protected <T extends Number & Comparable<? super T>> void
1068                    createNumberFieldWithTypedSortable(String name, T... values) {
1069    
1070                    createNumberField(name, true, values);
1071            }
1072    
1073            protected void createSortableKeywordField(String name, String value) {
1074                    if (isDocumentSortableTextField(name)) {
1075                            createSortableTextField(name, value);
1076                    }
1077            }
1078    
1079            protected void createSortableKeywordField(String name, String[] values) {
1080                    if (isDocumentSortableTextField(name)) {
1081                            createSortableTextField(name, values);
1082                    }
1083            }
1084    
1085            protected void createSortableNumericField(
1086                    String name, boolean typify, String value,
1087                    Class<? extends Number> clazz) {
1088    
1089                    if (typify) {
1090                            name = name.concat(StringPool.UNDERLINE).concat("Number");
1091                    }
1092    
1093                    String sortableFieldName = getSortableFieldName(name);
1094    
1095                    Field field = new Field(sortableFieldName, value);
1096    
1097                    _fields.put(sortableFieldName, field);
1098    
1099                    field.setNumeric(true);
1100                    field.setNumericClass(clazz);
1101            }
1102    
1103            protected <T extends Number & Comparable<? super T>> void
1104                    createSortableNumericField(String name, boolean typify, T... values) {
1105    
1106                    if ((values == null) || (values.length == 0)) {
1107                            return;
1108                    }
1109    
1110                    T minValue = Collections.min(Arrays.asList(values));
1111    
1112                    createSortableNumericField(
1113                            name, typify, String.valueOf(minValue), minValue.getClass());
1114            }
1115    
1116            protected void createSortableTextField(String name, String value) {
1117                    String truncatedValue = value;
1118    
1119                    if (value.length() > _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH) {
1120                            truncatedValue = value.substring(
1121                                    0, _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH);
1122                    }
1123    
1124                    createKeywordField(getSortableFieldName(name), truncatedValue, true);
1125            }
1126    
1127            protected void createSortableTextField(String name, String[] values) {
1128                    if (values.length == 0) {
1129                            return;
1130                    }
1131    
1132                    createSortableTextField(name, Collections.min(Arrays.asList(values)));
1133            }
1134    
1135            protected void setSortableTextFields(Set<String> sortableTextFields) {
1136                    _sortableTextFields = sortableTextFields;
1137            }
1138    
1139            private static final String _INDEX_DATE_FORMAT_PATTERN = PropsUtil.get(
1140                    PropsKeys.INDEX_DATE_FORMAT_PATTERN);
1141    
1142            private static final String _SORTABLE_FIELD_SUFFIX = "sortable";
1143    
1144            private static final int _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH =
1145                    GetterUtil.getInteger(
1146                            PropsUtil.get(
1147                                    PropsKeys.INDEX_SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH));
1148    
1149            private static final String _UID_FIELD = "_FIELD_";
1150    
1151            private static final String _UID_PORTLET = "_PORTLET_";
1152    
1153            private static Format _dateFormat =
1154                    FastDateFormatFactoryUtil.getSimpleDateFormat(
1155                            _INDEX_DATE_FORMAT_PATTERN);
1156            private static Set<String> _defaultSortableTextFields = SetUtil.fromArray(
1157                    PropsUtil.getArray(PropsKeys.INDEX_SORTABLE_TEXT_FIELDS));
1158    
1159            private Map<String, Field> _fields = new HashMap<String, Field>();
1160            private Set<String> _sortableTextFields = _defaultSortableTextFields;
1161    
1162    }