/* * Firetweet - Twitter client for Android * * Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 General Public License for more details. * * 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.getlantern.firetweet.util; import android.util.Log; import org.getlantern.firetweet.FiretweetConstants; import java.io.IOException; /** * Created by mariotaku on 15/2/13. */ public class SimpleValueSerializer { private static final char ESCAPE = '\\'; private static final char DIVIDER_ARRAY_ELEMENT = ';'; private static final char DIVIDER_OBJECT_FIELD = ','; private static final char DIVIDER_KEY_VALUE = '='; public static Reader newReader(String str) { return new Reader(str); } private static <T extends SimpleValueSerializable> T[] append(T[] old, int position, T item, Creator<T> creator) { T[] appended; if (position < old.length) { appended = old; } else { appended = creator.newArray(old.length * 2); System.arraycopy(old, 0, appended, 0, old.length); } appended[position] = item; return appended; } public static String escape(String str) { final StringBuilder sb = new StringBuilder(); for (int i = 0, j = str.length(); i < j; i++) { final char ch = str.charAt(i); if (DIVIDER_ARRAY_ELEMENT == ch || ESCAPE == ch) { sb.append(ESCAPE); } sb.append(ch); } return sb.toString(); } @SafeVarargs public static <T extends SimpleValueSerializable> String toSerializedString(T... array) { final Writer writer = newWriter(); for (T item : array) { writer.beginArrayElement(); item.write(writer); } return writer.toString(); } public static <T extends SimpleValueSerializable> T[] fromSerializedString(final String json, Creator<T> creator) { long start = System.nanoTime(); T[] temp = creator.newArray(4); try { if (json == null) return null; final SimpleValueSerializer.Reader reader = SimpleValueSerializer.newReader(json); int count = 0; while (reader.hasArrayElement()) { final T item = creator.create(reader); reader.endArrayElement(); temp = append(temp, count++, item, creator); } final T[] result = creator.newArray(count); System.arraycopy(temp, 0, result, 0, count); return result; } catch (final IOException e) { e.printStackTrace(); return null; } } public interface Creator<T extends SimpleValueSerializable> { T create(Reader reader) throws SerializationException; T[] newArray(int size); } public static class SerializationException extends IOException { } public static Writer newWriter() { return new Writer(); } public static interface SimpleValueSerializable { void write(Writer writer); } public static class Writer { private int keyValuesCount; private final StringBuilder stringBuilder; public Writer() { stringBuilder = new StringBuilder(); keyValuesCount = 0; } public void beginArrayElement() { keyValuesCount = 0; if (stringBuilder.length() > 0) { stringBuilder.append(DIVIDER_ARRAY_ELEMENT); } } public void write(String key, String value) { if (keyValuesCount++ > 0) { stringBuilder.append(DIVIDER_OBJECT_FIELD); } stringBuilder.append(escape(key)); stringBuilder.append(DIVIDER_KEY_VALUE); stringBuilder.append(escape(value)); } public String toString() { return stringBuilder.toString(); } public void write(String key, long value) { write(key, String.valueOf(value)); } public void write(String key, boolean value) { write(key, String.valueOf(value)); } } public static class Reader { private final String s; private final int len; private char lastToken; private int cur; public Reader(String s) { this.s = s; this.len = s.length(); cur = 0; lastToken = '\0'; } public boolean hasKeyValue() { return (cur == 0 || (lastToken == '\0' || lastToken == DIVIDER_OBJECT_FIELD)) && cur < len; } public boolean hasArrayElement() { return (cur == 0 || (lastToken == '\0' || lastToken == DIVIDER_ARRAY_ELEMENT)) && cur < len; } public String nextKey() throws SerializationException { if (lastToken != '\0' && lastToken != DIVIDER_OBJECT_FIELD) { throw new SerializationException(); } return peek(); } private String peek() { StringBuilder sb = new StringBuilder(); boolean isEscaped = false; while (cur < len) { char ch = s.charAt(cur++); if (isEscaped) { sb.append(ch); isEscaped = false; } else if (ch == ESCAPE) { isEscaped = true; } else if (ch == DIVIDER_KEY_VALUE || ch == DIVIDER_OBJECT_FIELD || ch == DIVIDER_ARRAY_ELEMENT) { lastToken = ch; break; } else { sb.append(ch); } } return sb.toString(); } public void skipValue() throws SerializationException { nextString(); } public String nextString() throws SerializationException { if (lastToken != DIVIDER_KEY_VALUE) { throw new SerializationException(); } return peek(); } public int nextInt() throws SerializationException { return Integer.parseInt(nextString()); } public long nextLong() throws SerializationException { return Long.parseLong(nextString()); } public void endArrayElement() { lastToken = '\0'; } } }