Skip to content

Commit

Permalink
Parsers draft + adding the Utils to the project - workaround for :
Browse files Browse the repository at this point in the history
http://stackoverflow.com/questions/18981837/
android-library-project-does-not-export-its-class-dependencies
  • Loading branch information
Utumno committed Sep 26, 2013
1 parent f7b9390 commit c8d1fc2
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 50 deletions.
1 change: 0 additions & 1 deletion .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry combineaccessrules="false" kind="src" path="/Helpers"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
bin/
~/
92 changes: 92 additions & 0 deletions src/gr/uoa/di/java/helpers/Utils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package gr.uoa.di.java.helpers;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public final class Utils {

private Utils() {}

// Standard charsets' NAMES
// see http://stackoverflow.com/questions/1684040/ for a discussion
// in Java 7 there is a StandardCharsets class :
// docs.oracle.com/javase/7/docs/api/java/nio/charset/StandardCharsets.html
// ---------------------------------------------------------------------- //
/**
* Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the
* Unicode character set
*/
public static final String ASCII = "US-ASCII";
/** ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1 */
public static final String ISO8859 = "ISO-8859-1";
/** Eight-bit UCS Transformation Format */
public static final String UTF8 = "UTF-8";
/** Sixteen-bit UCS Transformation Format, big-endian byte order */
public static final String UTF16BE = "UTF-16BE";
/** Sixteen-bit UCS Transformation Format, little-endian byte order */
public static final String UTF16LE = "UTF-16LE";
/**
* Sixteen-bit UCS Transformation Format, byte order identified by an
* optional byte-order mark
*/
public static final String UTF16 = "UTF-16";

public static String listToString(List<Byte> lb, String charsetName)
throws UnsupportedEncodingException, NumberFormatException {
// see http://stackoverflow.com/questions/1096868/
// TODO better tests
if (lb == null)
throw new NullPointerException("List<Byte> can't be null");
byte[] array = new byte[lb.size()];
{
int i = 0;
for (byte current : lb) {
array[i] = current;
++i;
}
}
return new String(array, charsetName);
}

public static long listToLong(List<Byte> lb)
throws UnsupportedEncodingException, NumberFormatException {
// TODO better tests
if (lb == null)
throw new NullPointerException("List<Byte> can't be null");
return new BigDecimal(listToString(lb, ASCII)).longValue();
}

/**
* If list is empty 0 is returned. If List is null is thrown
*
* @param lb
* @return
* @throws UnsupportedEncodingException
* @throws NullPointerException
* if ld is null
*/
public static double listToDouble(List<Byte> lb)
throws UnsupportedEncodingException, NumberFormatException {
// TODO better tests
if (lb == null)
throw new NullPointerException("List<Byte> can't be null");
return new BigDecimal(listToString(lb, ASCII)).doubleValue();
}

public static List<Byte> listFromArray(byte[] ba) {
if (ba == null) throw new NullPointerException("byte[] can't be null");
final List<Byte> lb = new ArrayList<Byte>();
for (byte b : ba) {
lb.add(b);
}
return lb;
}
// public static <T> T[] concat(T[] first, T[] second) {
// T[] result = Arrays.copyOf(first, first.length + second.length); // NOT
// // fucki9ng available
// System.arraycopy(second, 0, result, first.length, second.length);
// return result;
// }
}
211 changes: 163 additions & 48 deletions src/gr/uoa/di/monitoring/android/persist/FileStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,34 @@
import gr.uoa.di.android.helpers.FileIO;
import gr.uoa.di.android.helpers.Zip.CompressException;
import gr.uoa.di.java.helpers.Utils;
import gr.uoa.di.monitoring.model.Battery;
import gr.uoa.di.monitoring.model.Data;
import gr.uoa.di.monitoring.model.ParserException;
import gr.uoa.di.monitoring.model.Position;
import gr.uoa.di.monitoring.model.Wifi;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import android.content.Context; // TODO : .... coupling

public final class FileStore {

private static final String FILENAME_SEPA = "_";

private FileStore() {}

// TODO : can any of my data contain those delimiters ?
Expand All @@ -35,6 +47,24 @@ private FileStore() {}
*/
public static final String FILES_ENCODING = Utils.UTF8;
public static final Object FILE_STORE_LOCK = new Object();

/**
* Data Class + filename - would be great if overriding of static methods
* were permitted plus one could access them as Battery.getFilename()
* (getFilename() being static and abstract and defined in Data). Notice
* also that {@code Map<Class<? extends Enum<?> & Fields<?, ?, ?>>, String>}
* will fail to compile in a field declaration see <a
* href="http://stackoverflow.com/a/6643378/281545">here</a> TODO ask
*
* @return
*/
private final static Map<Class<? extends Data>, String> DATA_CLASSES = new HashMap<Class<? extends Data>, String>();
static {
DATA_CLASSES.put(Battery.class, new Battery("").getFilename());
DATA_CLASSES.put(Position.class, new Position("").getFilename());
DATA_CLASSES.put(Wifi.class, new Wifi("").getFilename());
}

// =========================================================================
// Persistence
// =========================================================================
Expand Down Expand Up @@ -77,6 +107,31 @@ private static synchronized File getRootFolder(Context ctx)
return sRootFolder;
}

/**
* Gets the device ID from the filename as it comes from the mobile
*
* @param filename
* just the filename not a path
* @return the deviceID
*/
public static String getDeviceID(String filename) {
// as it is the device ID is the first part of the filename
return filename.split(FILENAME_SEPA)[0];
}

/**
* Creates the filename for the zip to send to the server. For now it is in
* the format imei_currentTimeMillis
*
* @param rootPath
* the root folder the data are saved - the device ID (imei) -
* just the folder name not a path
* @return the filename
*/
private static String filename(String rootPath) {
return rootPath + FILENAME_SEPA + System.currentTimeMillis();
}

/**
* Returns a File instance representing a file in an internal directory. The
* internalDir specified must exist and be a directory.
Expand Down Expand Up @@ -239,8 +294,7 @@ public static List<File> internalFiles(Context ctx) throws IOException {
public static File file(Context ctx) throws IOException {
try {
final String rootPath = getRootFolder(ctx).getAbsolutePath();
final String destination = rootPath + "_"
+ System.currentTimeMillis();
final String destination = filename(rootPath);
return gr.uoa.di.android.helpers.Zip.zipFolder(rootPath,
destination).getFile();
// final String backFilename = System.currentTimeMillis() + ".zip";
Expand Down Expand Up @@ -373,6 +427,41 @@ public static interface Fields<T, D extends Data, K> {
D parse(K list, D objectToModify) throws ParserException;
}

// =========================================================================
// Parsers
// =========================================================================
public static <T extends Data> Map<Class<? extends Data>, List<T>> parse(
String rootPath, String imei) throws ParserException {
Map<Class<? extends Data>, List<T>> lol = new HashMap<Class<? extends Data>, List<T>>();
for (Entry<Class<? extends Data>, String> entry : DATA_CLASSES
.entrySet()) {
final File file = new File(rootPath, entry.getValue());
final Class<? extends Data> dataCls = entry.getKey();
Method pars = null;
try {
pars = dataCls.getMethod("parse", File.class, String.class);
} catch (NoSuchMethodException e) {
throw new ParserException("Reflection failure", e);
}
List<T> invoke = null;
try {
invoke = (List<T>) pars.invoke(null, file, imei);
} catch (IllegalArgumentException e) {
throw new ParserException("Reflection failure", e);
} catch (IllegalAccessException e) {
throw new ParserException("Reflection failure", e);
} catch (InvocationTargetException e) {
// FIXME - reflection is rather awkward - drop it ?
if (e.getCause() instanceof FileNotFoundException) {
// the file was not found - not a fatal error
invoke = Collections.emptyList();
}
}
lol.put(dataCls, invoke);
}
return lol;
}

/**
* Meant to be used in the server so uses Lists internally instead of
* arrays. Buffers the input stream so do not pass in a BufferedInputStream
Expand Down Expand Up @@ -409,65 +498,91 @@ public static interface Fields<T, D extends Data, K> {
}
// friggin FIXME : malformed files ? empty lines ?
final boolean hasLists = hasLists(fields);
List<D> daBytes;
if (hasLists) {
daBytes = (List<D>) new ArrayList<EnumMap<T, List<List<Byte>>>>();
} else {
daBytes = (List<D>) new ArrayList<EnumMap<T, List<Byte>>>();
// this is the only unchecked warning
@SuppressWarnings({ "unchecked", "rawtypes" })
GetEntries<D, T> getEntries = hasLists ? new GetByteListEntries(fields)
: new GetByteEntries(fields);
return getEntries.invoke(entries);
}

private abstract static class GetEntries<D, T extends Enum<T> & Fields<?, ?, D>> {

Class<T> fields;

public GetEntries(Class<T> fields) {
this.fields = fields;
}
final int numOfEntries = entries.size();
for (int currentEntry = 0; currentEntry < numOfEntries; ++currentEntry) {
List<Byte> entry = entries.get(currentEntry);
// add an element in daBytes for this entry
if (hasLists) {
daBytes.add((D) new EnumMap<T, List<List<Byte>>>(fields));
} else {
daBytes.add((D) new EnumMap<T, List<Byte>>(fields));
}
// and now the party - getting the actual fields for this entry
for (T daField : fields.getEnumConstants()) {
List<Byte> field = new ArrayList<Byte>();
{

public List<EnumMap<T, D>> invoke(List<List<Byte>> entries) {
final List<EnumMap<T, D>> daBytes = new ArrayList<EnumMap<T, D>>();
final int numOfEntries = entries.size();
for (int currentEntry = 0; currentEntry < numOfEntries; ++currentEntry) {
List<Byte> entry = entries.get(currentEntry);
daBytes.add(new EnumMap<T, D>(fields));
EnumMap<T, D> map = daBytes.get(currentEntry);
// and now the party - getting the actual fields for this entry
for (T daField : fields.getEnumConstants()) {
List<Byte> field = new ArrayList<Byte>();
int position = 0;
for (byte b : entry) {
++position;
if (b == DELIMITER) {
entry = new ArrayList<Byte>(entry.subList(position,
entry.size())); // chop off the bytes I read
break;
} else field.add(b);
}
}// got the field bytes
D map = daBytes.get(currentEntry);
if (hasLists) {
List<List<Byte>> fieldEntries = new ArrayList<List<Byte>>();
if (!daField.isList()) {
// one entry only
fieldEntries.add(field);
} else {
{
// parse the field
boolean kaboom = true;
int fieldEntry = -1;
for (byte b : field) {
if (kaboom) {
fieldEntries.add(new ArrayList<Byte>());
kaboom = false;
++fieldEntry;
}
if (b == ARRAY_DELIMITER) kaboom = true;
else fieldEntries.get(fieldEntry).add(b);
}
}
field.add(b);
} // got the field bytes
map.put(daField, getFieldData(daField, field));
}
}
return daBytes;
}

abstract D getFieldData(T daField, List<Byte> field);
}

private final static class GetByteEntries<T extends Enum<T> & Fields<?, ?, List<Byte>>>
extends GetEntries<List<Byte>, T> {

public GetByteEntries(Class<T> fields) {
super(fields);
}

@Override
List<Byte> getFieldData(T daField, List<Byte> field) {
return field;
}
}

private final static class GetByteListEntries<T extends Enum<T> & Fields<?, ?, List<List<Byte>>>>
extends GetEntries<List<List<Byte>>, T> {

public GetByteListEntries(Class<T> fields) {
super(fields);
}

@Override
List<List<Byte>> getFieldData(T daField, List<Byte> field) {
final List<List<Byte>> fieldEntries = new ArrayList<List<Byte>>();
if (!daField.isList()) {
fieldEntries.add(field); // one entry only
} else {
// parse the field
boolean kaboom = true;
int fieldEntry = -1;
for (byte b : field) {
if (kaboom) {
fieldEntries.add(new ArrayList<Byte>());
kaboom = false;
++fieldEntry;
}
((EnumMap<T, List<List<Byte>>>) map).put(daField,
fieldEntries);
} else {
((EnumMap<T, List<Byte>>) map).put(daField, field);
if (b == ARRAY_DELIMITER) kaboom = true;
else fieldEntries.get(fieldEntry).add(b);
}
}
return fieldEntries;
}
return (List<EnumMap<T, D>>) daBytes;
}

// That's what you get for nor being able to override static methods
Expand Down

0 comments on commit c8d1fc2

Please sign in to comment.