/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.datasource.csv.converter; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nullable; import org.obiba.magma.Attribute; import org.obiba.magma.AttributeAware; import org.obiba.magma.Attributes; import org.obiba.magma.Category; import org.obiba.magma.ValueType; import org.obiba.magma.Variable; import org.obiba.magma.support.DatasourceParsingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Strings; import com.google.common.collect.Lists; public class VariableConverter { private static final Logger log = LoggerFactory.getLogger(VariableConverter.class); public static final String NAME = "name"; public static final String VALUE_TYPE = "valueType"; public static final String ENTITY_TYPE = "entityType"; public static final String REFERENCED_ENTITY_TYPE = "referencedEntityType"; public static final String MIME_TYPE = "mimeType"; public static final String REPEATABLE = "repeatable"; public static final String OCCURRENCE_GROUP = "occurrenceGroup"; public static final String UNIT = "unit"; public static final String INDEX = "index"; public static final String CATEGORIES = "categories"; public static final String LABEL = "label"; public static final List<String> reservedVariableHeaders = Lists.newArrayList(NAME, // VALUE_TYPE, // ENTITY_TYPE, // REFERENCED_ENTITY_TYPE, // MIME_TYPE, // UNIT, // REPEATABLE, // OCCURRENCE_GROUP, // INDEX); public static final List<String> categoriesReservedAttributeNames = Lists .newArrayList("table", "variable", "name", "code", "missing"); private final Map<String, Integer> headerMap = new HashMap<>(); private final String[] header; public VariableConverter(String... headers) { header = new String[headers.length]; for(int i = 0; i < headers.length; i++) { String headerColumnName = headers[i].trim(); headerMap.put(headerColumnName, i); header[i] = headerColumnName; } if(log.isTraceEnabled()) { for(Entry<String, Integer> entry : headerMap.entrySet()) { log.trace("headerMap[{}]={}", entry.getKey(), entry.getValue()); } } validateHeader(); } private void validateHeader() { if(!headerMap.containsKey(NAME)) throw new DatasourceParsingException("The variables.csv header must contain 'name'.", "CsvVariablesHeaderMustContainName"); if(!headerMap.containsKey(VALUE_TYPE)) throw new DatasourceParsingException("The variables.csv header must contain 'valueType'.", "CsvVariablesHeaderMustContainValueType"); if(!headerMap.containsKey(ENTITY_TYPE)) throw new DatasourceParsingException("The variables.csv header must contain 'entityType'.", "CsvVariablesHeaderMustContainEntityType"); } @SuppressWarnings({ "PMD.NcssMethodCount", "OverlyLongMethod" }) public Variable unmarshal(String... csvVar) { String name = getValueAt(csvVar, NAME); String valueType = getValueAt(csvVar, VALUE_TYPE); String entityType = getValueAt(csvVar, ENTITY_TYPE); String referencedEntityType = getValueAt(csvVar, REFERENCED_ENTITY_TYPE); String mimeType = getValueAt(csvVar, MIME_TYPE); String unit = getValueAt(csvVar, UNIT); String repeatable = getValueAt(csvVar, REPEATABLE); String occurrenceGroup = getValueAt(csvVar, OCCURRENCE_GROUP); String index = getValueAt(csvVar, INDEX); log.debug("name={} valueType={} entityType={} mimeType={}", name, valueType, entityType, mimeType); Variable.Builder builder = Variable.Builder.newVariable(name, ValueType.Factory.forName(valueType), entityType) .mimeType(mimeType).unit(unit).occurrenceGroup(occurrenceGroup).referencedEntityType(referencedEntityType); if(Boolean.parseBoolean(repeatable)) { builder.repeatable(); } if(index != null) { try { builder.index(Integer.valueOf(index)); } catch(NumberFormatException e) { // ignore } } // attributes and categories Map<String, Category.Builder> categoryBuilderMap = unmarshalCategoriesAndAttributes(builder, csvVar); for(Map.Entry<String, Category.Builder> entry : categoryBuilderMap.entrySet()) { builder.addCategory(entry.getValue().build()); } return builder.build(); } private Map<String, Category.Builder> unmarshalCategoriesAndAttributes(Variable.Builder builder, String... csvVar) { Map<String, Category.Builder> categoryBuilderMap = new LinkedHashMap<>(); for(String headerName : headerMap.keySet()) { if(!reservedVariableHeaders.contains(headerName)) { String value = getValueAt(csvVar, headerName); if(value != null) { if(headerName.startsWith(CATEGORIES)) { unmarshalCategories(value, getAttributeLocale(headerName), categoryBuilderMap); } else { Attribute.Builder attrBuilder = Attributes.decodeFromHeader(headerName); builder.addAttribute(attrBuilder.withValue(value).build()); } } } } return categoryBuilderMap; } private void unmarshalCategories(String categories, @Nullable Locale locale, Map<String, Category.Builder> categoryBuilderMap) { String[] cats = categories.split(";"); for(String cat1 : cats) { String[] cat = cat1.trim().split("="); String catName = cat[0].trim(); Category.Builder catBuilder; if(categoryBuilderMap.containsKey(catName)) { catBuilder = categoryBuilderMap.get(catName); } else { catBuilder = Category.Builder.newCategory(catName); categoryBuilderMap.put(catName, catBuilder); } if(cat.length > 1) catBuilder.addAttribute(LABEL, cat[1].trim(), locale); } } private String marshalCategories(Variable variable, Locale locale) { StringBuilder sb = new StringBuilder(); for(Category category : variable.getCategories()) { sb.append(category.getName()); Attribute label = null; if(category.hasAttribute(LABEL)) { label = locale != null && category.hasAttribute(LABEL, locale) ? category.getAttribute(LABEL, locale) : category.getAttribute(LABEL); } if(label != null) { sb.append("=").append(label.getValue()); } sb.append(";"); // TODO configure separator. } if(sb.length() > 0) sb.setLength(sb.length() - 1); // Remove last separator. return sb.toString(); } @Nullable private Locale getAttributeLocale(String header) { String[] h = header.split(":"); if(h.length > 1 && !h[1].trim().isEmpty()) { return new Locale(h[1].trim()); } return null; } @Nullable private String getValueAt(String[] csvVar, String header) { String value = null; Integer pos = headerMap.get(header); if(pos != null && pos < csvVar.length) { value = csvVar[pos]; } return Strings.emptyToNull(value); } @SuppressWarnings({ "OverlyLongMethod", "PMD.NcssMethodCount" }) public String[] marshal(Variable variable) { Map<Integer, String> resultMap = new HashMap<>(); resultMap.put(headerMap.get(NAME), variable.getName()); resultMap.put(headerMap.get(VALUE_TYPE), variable.getValueType().getName()); resultMap.put(headerMap.get(ENTITY_TYPE), variable.getEntityType()); if(headerMap.containsKey(MIME_TYPE)) { resultMap.put(headerMap.get(MIME_TYPE), variable.getMimeType()); } if(headerMap.containsKey(REPEATABLE)) { resultMap.put(headerMap.get(REPEATABLE), Boolean.toString(variable.isRepeatable())); } if(headerMap.containsKey(OCCURRENCE_GROUP)) { resultMap.put(headerMap.get(OCCURRENCE_GROUP), variable.getOccurrenceGroup()); } if(headerMap.containsKey(UNIT)) { resultMap.put(headerMap.get(UNIT), variable.getUnit()); } if(headerMap.containsKey(REFERENCED_ENTITY_TYPE)) { resultMap.put(headerMap.get(REFERENCED_ENTITY_TYPE), variable.getReferencedEntityType()); } marshalAttributes(variable, resultMap); marshalCategories(variable, resultMap); int size = headerMap.size(); String[] result = new String[size]; for(int i = 0; i < size; i++) { if(resultMap.containsKey(i)) { result[i] = resultMap.get(i); } } return result; } private void marshalCategories(Variable variable, Map<Integer, String> resultMap) { for(String header : headerMap.keySet()) { if(header.startsWith(CATEGORIES)) { resultMap.put(headerMap.get(header), marshalCategories(variable, getAttributeLocale(header))); } } } private void marshalAttributes(AttributeAware variable, Map<Integer, String> resultMap) { for(Attribute attribute : variable.getAttributes()) { String header = Attributes.encodeForHeader(attribute); if(headerMap.containsKey(header) && !reservedVariableHeaders.contains(header)) { resultMap.put(headerMap.get(header), attribute.getValue().toString()); } } } public String[] getHeader() { return Arrays.copyOf(header, header.length); } }