// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.slim.protocol; import java.util.ArrayList; import java.util.List; import fitnesse.slim.SlimVersion; /** * Uses Slim Serialization. See SlimSerializer for details. Will deserialize lists of lists recursively. */ public class SlimDeserializer { private List<Object> result; public static List<Object> deserialize(String serialized) { return new SlimDeserializer(serialized).deserialize(); } private String serialized; private int index = 0; public SlimDeserializer(String serialized) { this.serialized = serialized; } public List<Object> deserialize() { try { checkSerializedStringIsValid(); return deserializeString(); } catch (Exception e) { throw new SyntaxError(e); } } private void checkSerializedStringIsValid() { if (serialized == null) throw new SyntaxError("Can't deserialize null"); else if (serialized.isEmpty()) throw new SyntaxError("Can't deserialize empty string"); } private List<Object> deserializeString() { checkForOpenBracket(); List<Object> result = deserializeList(); checkForClosedBracket(); return result; } private void checkForClosedBracket() { if (!charsLeft() || getChar() != ']') throw new SyntaxError("Serialized list has no ending ]"); } private boolean charsLeft() { return index < serialized.length(); } private void checkForOpenBracket() { if (getChar() != '[') throw new SyntaxError("Serialized list has no starting ["); } private List<Object> deserializeList() { result = new ArrayList<>(); int itemCount = getLength(); for (int i = 0; i < itemCount; i++) deserializeItem(); return result; } private void deserializeItem() { int itemLength = getLength(); String item = getString(itemLength); List<Object> sublist = maybeReadList(item); if (sublist == null) result.add(item); else result.add(sublist); } /** * @return the string parsed as a list if possible, null otherwise */ private List<Object> maybeReadList(String string) { if ("".equals(string.trim()) || !string.startsWith("[")) return null; try { return SlimDeserializer.deserialize(string); } catch (SyntaxError e) { return null; } } private String getString(int length) { String result = serialized.substring(index, index + length); index += length; checkForColon("String"); return result; } private void checkForColon(String itemType) { if (getChar() != ':') throw new SyntaxError(itemType + " in serialized list not terminated by colon."); } private char getChar() { return serialized.charAt(index++); } private int getLength() { try { return tryGetLength(); } catch (NumberFormatException e) { throw new SyntaxError(e); } } private int tryGetLength() { int lengthSize = SlimVersion.MINIMUM_NUMBER_LENGTH; String lengthString = serialized.substring(index, index + lengthSize); int length = Integer.parseInt(lengthString); index += lengthSize; Integer next; while ((next = maybeReadDigit()) != null) length = length * 10 + next; checkForColon("Length"); return length; } private Integer maybeReadDigit() { char next = serialized.charAt(index); if (Character.isDigit(next)) { index++; return Character.digit(next, 10); } else return null; } }