package org.activityinfo.core.shared.importing.match; /* * #%L * ActivityInfo Server * %% * Copyright (C) 2009 - 2013 UNICEF * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.activityinfo.model.form.FormField; import org.activityinfo.model.form.FormFieldType; import org.activityinfo.model.type.FieldTypeClass; import org.activityinfo.core.shared.type.converter.Converter; import org.activityinfo.core.shared.type.converter.ConverterFactory; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; /** * Guesser use converters to guess column type. * <p/> * Note: If we can NOT convert value to particular type then we * don't have other choice then consider it as string. * * @author yuriyz on 5/9/14. */ public class ColumnTypeGuesser { private final Map<FieldTypeClass, Integer> typeMap = newFieldMap(); private final List<String> columnValues; private final ConverterFactory converterFactory; public ColumnTypeGuesser(List<String> columnValues, ConverterFactory converterFactory) { this.columnValues = columnValues; this.converterFactory = converterFactory; } public FieldTypeClass guessType() { calculateTypeScores(); final List<Map.Entry<FieldTypeClass, Integer>> copyEntrySet = Lists.newArrayList(typeMap.entrySet()); Collections.sort(copyEntrySet, new Comparator<Map.Entry<FieldTypeClass, Integer>>() { @Override public int compare(Map.Entry<FieldTypeClass, Integer> o1, Map.Entry<FieldTypeClass, Integer> o2) { return o1.getValue().compareTo(o2.getValue()); } }); return copyEntrySet.get(copyEntrySet.size() - 1).getKey(); } private void calculateTypeScores() { for (String value : columnValues) { final Map<FieldTypeClass, Integer> copyMap = Maps.newHashMap(typeMap); // we don't need to iterate over string types because input is always string copyMap.remove(FieldTypeClass.FREE_TEXT); copyMap.remove(FieldTypeClass.NARRATIVE); boolean hasMatch = false; for (Map.Entry<FieldTypeClass, Integer> entry : copyMap.entrySet()) { try { final Converter stringConverter = converterFactory.createStringConverter(entry.getKey()); final Object convertedValue = stringConverter.convert(value); // analyze converted value if (convertedValue != null) { increaseValue(entry.getKey()); hasMatch = true; break; } } catch (Exception e) { // ignore } } // if no match then we fallback to string type if (!hasMatch) { final int length = value.length(); if (length < FormFieldType.FREE_TEXT_LENGTH) { increaseValue(FieldTypeClass.FREE_TEXT); } else { increaseValue(FieldTypeClass.NARRATIVE); } } } } private void increaseValue(FieldTypeClass type) { typeMap.put(type, typeMap.get(type) + 1); } private static Map<FieldTypeClass, Integer> newFieldMap() { Map<FieldTypeClass, Integer> typeMap = Maps.newHashMap(); for (FieldTypeClass type : FormFieldType.values()) { typeMap.put(type, 0); } return typeMap; } }