/** * AnalyzerBeans * Copyright (C) 2014 Neopost - Customer Information Management * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.eobjects.analyzer.util.convert; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eobjects.analyzer.beans.api.Converter; import org.eobjects.analyzer.util.CharIterator; import org.eobjects.analyzer.util.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A {@link Converter} implementation for array types. */ public class ArrayConverter implements Converter<Object> { private static final Logger logger = LoggerFactory.getLogger(ArrayConverter.class); private final Converter<Object> _parentConvert; public ArrayConverter(Converter<Object> parentConverter) { _parentConvert = parentConverter; } @Override public Object fromString(Class<?> type, String str) { final boolean isList = ReflectionUtils.is(type, List.class); if (isList) { // Warning: We only support string lists, since component type is // not determinable from List.class. type = String[].class; } str = str.trim(); Object result = fromStringInternal(type, str); if (isList) { String[] array = (String[]) result; result = Arrays.asList(array); } return result; } public Object fromStringInternal(Class<?> type, String str) { assert type.isArray(); final Class<?> componentType = type.getComponentType(); if ("[]".equals(str)) { logger.debug("found [], returning empty array"); return Array.newInstance(componentType, 0); } if (logger.isDebugEnabled()) { logger.debug("deserializeArray(\"{}\")", str); logger.debug("component type is: {}", componentType); int beginningBrackets = 0; int endingBrackets = 0; CharIterator it = new CharIterator(str); while (it.hasNext()) { it.next(); if (it.is('[')) { beginningBrackets++; } else if (it.is(']')) { endingBrackets++; } } it.reset(); logger.debug("brackets statistics: beginning={}, ending={}", beginningBrackets, endingBrackets); if (beginningBrackets != endingBrackets) { logger.warn("Unbalanced beginning and ending brackets!"); } } if (!str.startsWith("[") || !str.endsWith("]")) { if (str.indexOf(',') == -1) { Object result = Array.newInstance(componentType, 1); Object singleItem = _parentConvert.fromString(componentType, str); Array.set(result, 0, singleItem); return result; } throw new IllegalArgumentException( "Cannot parse string as array, bracket encapsulation and comma delimitors expected. Found: " + str); } final String innerString = str.substring(1, str.length() - 1); logger.debug("innerString: {}", innerString); List<Object> objects = new ArrayList<Object>(); int offset = 0; while (offset < innerString.length()) { logger.debug("offset: {}", offset); final int commaIndex = innerString.indexOf(',', offset); logger.debug("commaIndex: {}", commaIndex); final int bracketBeginIndex = innerString.indexOf('[', offset); logger.debug("bracketBeginIndex: {}", bracketBeginIndex); if (commaIndex == -1) { logger.debug("no comma found"); String s = innerString.substring(offset); Object item = _parentConvert.fromString(componentType, s); objects.add(item); offset = innerString.length(); } else if (bracketBeginIndex == -1 || commaIndex < bracketBeginIndex) { String s = innerString.substring(offset, commaIndex); if ("".equals(s)) { offset++; } else { logger.debug("no brackets in next element: \"{}\"", s); Object item = _parentConvert.fromString(componentType, s); objects.add(item); offset = commaIndex + 1; } } else { String s = innerString.substring(bracketBeginIndex); int nextBracket = 0; int depth = 1; logger.debug("substring with nested array: {}", s); while (depth > 0) { final int searchOffset = nextBracket + 1; int nextEndBracket = s.indexOf(']', searchOffset); if (nextEndBracket == -1) { throw new IllegalStateException("No ending bracket in array string: " + s.substring(searchOffset)); } int nextBeginBracket = s.indexOf('[', searchOffset); if (nextBeginBracket == -1) { nextBeginBracket = s.length(); } nextBracket = Math.min(nextEndBracket, nextBeginBracket); char c = s.charAt(nextBracket); logger.debug("nextBracket: {} ({})", nextBracket, c); if (c == '[') { depth++; } else if (c == ']') { depth--; } else { throw new IllegalStateException("Unexpected char: " + c); } logger.debug("depth: {}", depth); if (depth == 0) { s = s.substring(0, nextBracket + 1); logger.debug("identified array: {}", s); } } logger.debug("recursing to nested array: {}", s); logger.debug("inner array string: " + s); Object item = _parentConvert.fromString(componentType, s); objects.add(item); offset = bracketBeginIndex + s.length(); } } Object result = Array.newInstance(componentType, objects.size()); for (int i = 0; i < objects.size(); i++) { Array.set(result, i, objects.get(i)); } return result; } @Override public String toString(Object o) { assert o != null; assert o.getClass().isArray() || o instanceof List; if (o instanceof List) { o = ((List<?>) o).toArray(); } StringBuilder sb = new StringBuilder(); int length = Array.getLength(o); sb.append('['); for (int i = 0; i < length; i++) { Object obj = Array.get(o, i); if (i != 0) { sb.append(','); } sb.append(_parentConvert.toString(obj)); } sb.append(']'); return sb.toString(); } @Override public boolean isConvertable(Class<?> type) { return type.isArray() || ReflectionUtils.is(type, List.class); } }