/* * #%L * ACS AEM Tools Bundle * %% * Copyright (C) 2015 Adobe * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ package com.adobe.acs.tools.csv.impl; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.joda.time.format.ISODateTimeFormat; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; public final class Column<T> { private static final String MULTI = "multi"; private static final Pattern ISO_DATE_PATTERN = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}[-+]{1}[0-9]{2}[:]{0,1}[0-9]{2}$"); private String multiDelimiter = "|"; private String raw; private String propertyName; private boolean multi = false; private Class dataType = String.class; private int index = 0; private boolean ignore = false; public Column(final String raw, final int index) { this.index = index; this.raw = StringUtils.trim(raw); String paramsStr = StringUtils.substringBetween(raw, "{{", "}}"); String[] params = StringUtils.split(paramsStr, ":"); if (StringUtils.isBlank(paramsStr)) { this.propertyName = this.getRaw(); } else { this.propertyName = StringUtils.trim(StringUtils.substringBefore(this.getRaw(), "{{")); if (params.length == 2) { this.dataType = nameToClass(StringUtils.stripToEmpty(params[0])); this.multi = StringUtils.equalsIgnoreCase(StringUtils.stripToEmpty(params[1]), MULTI); } if (params.length == 1) { if (StringUtils.equalsIgnoreCase(MULTI, StringUtils.stripToEmpty(params[0]))) { this.multi = true; } else { this.dataType = nameToClass(StringUtils.stripToEmpty(params[0])); } } } } public T getData(String data) { return (T) toObjectType(data, this.getDataType()); } public T[] getMultiData(String data) { final String[] vals = StringUtils.split(data, this.multiDelimiter); final List<T> list = new ArrayList<T>(); for (String val : vals) { T obj = (T) this.toObjectType(val, this.getDataType()); list.add(obj); } return list.toArray((T[]) Array.newInstance((this.getDataType()), 0)); } private <T> T toObjectType(String data, Class<T> klass) { data = StringUtils.trim(data); if (Double.class.equals(klass)) { try { return klass.cast(Double.parseDouble(data)); } catch (NumberFormatException ex) { return null; } } else if (Long.class.equals(klass)) { try { return klass.cast(Long.parseLong(data)); } catch (NumberFormatException ex) { return null; } } else if (Integer.class.equals(klass)) { try { return klass.cast(Long.parseLong(data)); } catch (NumberFormatException ex) { return null; } } else if (StringUtils.equalsIgnoreCase("true", data)) { return klass.cast(Boolean.TRUE); } else if (StringUtils.equalsIgnoreCase("false", data)) { return klass.cast(Boolean.FALSE); } else if ((Date.class.equals(Date.class) || Calendar.class.equals(Calendar.class)) && ISO_DATE_PATTERN.matcher(data).matches()) { return klass.cast(ISODateTimeFormat.dateTimeParser().parseDateTime(data).toCalendar(Locale.US)); } else { return klass.cast(data); } } private Class nameToClass(String name) { if (StringUtils.equalsIgnoreCase(name, "date") || StringUtils.equalsIgnoreCase(name, "calendar")) { return Calendar.class; } else if (StringUtils.equalsIgnoreCase(name, "double")) { return Double.class; } else if (StringUtils.equalsIgnoreCase(name, "long") || StringUtils.equalsIgnoreCase(name, "int") || StringUtils.equalsIgnoreCase(name, "integer")) { return Long.class; } else if (StringUtils.equalsIgnoreCase(name, "boolean")) { return Boolean.class; } else { return String.class; } } public String getRaw() { return raw; } public String getPropertyName() { return propertyName; } public boolean isMulti() { return multi; } public Class getDataType() { return dataType; } public int getIndex() { return index; } public boolean isIgnore() { return ignore; } public void setIgnore(final boolean ignore) { this.ignore = ignore; } public void setMultiDelimiter(final String multiDelimiter) { this.multiDelimiter = multiDelimiter; } public static Map<String, Column> getColumns(final String[] row, final String multiDelimiter, final String[] ignoreProperties, final String[] requiredProperties) throws CsvException { final Map<String, Column> map = new HashMap<String, Column>(); for (int i = 0; i < row.length; i++) { final Column col = new Column(row[i], i); col.setIgnore(ArrayUtils.contains(ignoreProperties, col.getPropertyName())); col.setMultiDelimiter(multiDelimiter); map.put(col.getPropertyName(), col); } final List<String> missingRequiredProperties = hasRequiredFields(map.values(), requiredProperties); if (!missingRequiredProperties.isEmpty()) { throw new CsvException("Could not find required columns in CSV: " + StringUtils.join(missingRequiredProperties, ", ")); } return map; } private static List<String> hasRequiredFields(final Collection<Column> columns, final String... requiredPropertyNames) { final List<String> missing = new ArrayList<String>(); for (final String propertyName : requiredPropertyNames) { boolean found = false; for (final Column column : columns) { if (StringUtils.equals(propertyName, column.getPropertyName())) { found = true; break; } } if (!found) { missing.add(propertyName); } } return missing; } }