package org.archstudio.bna.logics.hints.coders; import java.util.Arrays; import java.util.List; import java.util.Set; import org.archstudio.bna.logics.hints.EncodedValue; import org.archstudio.bna.logics.hints.IEncodedValue; import org.archstudio.bna.logics.hints.IPropertyCoder; import org.archstudio.bna.logics.hints.PropertyDecodeException; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Sets; public class CollectionPropertyCoder implements IPropertyCoder { private static final String escape(String s) { StringBuffer sb = new StringBuffer(s.length() * 2); for (char ch : s.toCharArray()) { switch (ch) { case '\\': sb.append("\\\\"); break; case ';': sb.append("\\,"); break; case '{': sb.append("\\("); break; case '}': sb.append("\\)"); break; default: sb.append(ch); break; } } return sb.toString(); } private static final String unescape(String s) { StringBuffer sb = new StringBuffer(s.length()); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (ch) { case '\\': char ch2 = s.charAt(++i); switch (ch2) { case ',': sb.append(';'); break; case '(': sb.append('{'); break; case ')': sb.append('}'); break; default: sb.append(ch2); break; } break; default: sb.append(ch); break; } } return sb.toString(); } public CollectionPropertyCoder() { } private IEncodedValue encode(IPropertyCoder masterCoder, String type, Iterable<?> values) { List<String> encodedTypes = Lists.newArrayList(); List<String> encodedValues = Lists.newArrayList(); for (Object value : values) { IEncodedValue ev = masterCoder.encode(masterCoder, value); encodedTypes.add(escape(ev.getType())); encodedValues.add(escape(ev.getData())); } String encodedType = Joiner.on(";").join(encodedTypes); String encodedData = Joiner.on(";").join(encodedValues); return new EncodedValue(type + "{" + encodedType + "}", encodedData); } @Override public IEncodedValue encode(IPropertyCoder masterCoder, Object value) { if (value.getClass().isArray()) { return encode(masterCoder, "Array", Arrays.asList((Object[]) value)); } if (value instanceof List) { return encode(masterCoder, "List", (List<?>) value); } if (value instanceof Set) { return encode(masterCoder, "Set", (List<?>) value); } return null; } private List<Object> decode(IPropertyCoder masterCoder, String type, String data) throws PropertyDecodeException { String[] splitTypes = type.length() > 0 ? type.split(";") : new String[0]; String[] splitValues = data.length() > 0 ? data.split(";") : new String[0]; List<Object> values = Lists.newArrayList(); for (int i = 0; i < splitTypes.length && i < splitValues.length; i++) { String unescapedType = unescape(splitTypes[i]); String unescapedValue = unescape(splitValues[i]); IEncodedValue encodedValue = new EncodedValue(unescapedType, unescapedValue); values.add(masterCoder.decode(masterCoder, encodedValue)); } return values; } @Override public Object decode(IPropertyCoder masterCoder, IEncodedValue encodedValue) throws PropertyDecodeException { try { String type = encodedValue.getType(); String data = encodedValue.getData(); if (type.startsWith("Array{")) { return Lists.newArrayList(decode(masterCoder, type.substring(6, type.length() - 1), data)).toArray(); } if (type.startsWith("List{")) { return Lists.newArrayList(decode(masterCoder, type.substring(5, type.length() - 1), data)); } if (type.startsWith("Set{")) { return Sets.newHashSet(decode(masterCoder, type.substring(4, type.length() - 1), data)); } return null; } catch (Throwable t) { throw new PropertyDecodeException(t); } } }