package com.smartandroid.sa.sql;
/*
* Copyright (C) 2010 Michael Pardo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.Context;
import com.smartandroid.sa.sql.serializer.CalendarSerializer;
import com.smartandroid.sa.sql.serializer.FileSerializer;
import com.smartandroid.sa.sql.serializer.SqlDateSerializer;
import com.smartandroid.sa.sql.serializer.TypeSerializer;
import com.smartandroid.sa.sql.serializer.UtilDateSerializer;
import com.smartandroid.sa.sql.util.Log;
import com.smartandroid.sa.sql.util.ReflectionUtils;
import dalvik.system.DexFile;
final class ModelInfo {
// ////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
// ////////////////////////////////////////////////////////////////////////////////////
private Map<Class<? extends Model>, TableInfo> mTableInfos = new HashMap<Class<? extends Model>, TableInfo>();
private Map<Class<?>, TypeSerializer> mTypeSerializers = new HashMap<Class<?>, TypeSerializer>() {
{
put(Calendar.class, new CalendarSerializer());
put(java.sql.Date.class, new SqlDateSerializer());
put(java.util.Date.class, new UtilDateSerializer());
put(java.io.File.class, new FileSerializer());
}
};
// ////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
// ////////////////////////////////////////////////////////////////////////////////////
public ModelInfo(Configuration configuration) {
if (!loadModelFromMetaData(configuration)) {
try {
scanForModel(configuration.getContext());
} catch (IOException e) {
Log.e("Couldn't open source path.", e);
}
}
Log.i("ModelInfo loaded.");
}
// ////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
// ////////////////////////////////////////////////////////////////////////////////////
public Collection<TableInfo> getTableInfos() {
return mTableInfos.values();
}
public TableInfo getTableInfo(Class<? extends Model> type) {
return mTableInfos.get(type);
}
public TypeSerializer getTypeSerializer(Class<?> type) {
return mTypeSerializers.get(type);
}
// ////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
// ////////////////////////////////////////////////////////////////////////////////////
private boolean loadModelFromMetaData(Configuration configuration) {
if (!configuration.isValid()) {
return false;
}
final List<Class<? extends Model>> models = configuration
.getModelClasses();
if (models != null) {
for (Class<? extends Model> model : models) {
mTableInfos.put(model, new TableInfo(model));
}
}
final List<Class<? extends TypeSerializer>> typeSerializers = configuration
.getTypeSerializers();
if (typeSerializers != null) {
for (Class<? extends TypeSerializer> typeSerializer : typeSerializers) {
try {
TypeSerializer instance = typeSerializer.newInstance();
mTypeSerializers.put(instance.getDeserializedType(),
instance);
} catch (InstantiationException e) {
Log.e("Couldn't instantiate TypeSerializer.", e);
} catch (IllegalAccessException e) {
Log.e("IllegalAccessException", e);
}
}
}
return true;
}
private void scanForModel(Context context) throws IOException {
String packageName = context.getPackageName();
String sourcePath = context.getApplicationInfo().sourceDir;
List<String> paths = new ArrayList<String>();
if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
DexFile dexfile = new DexFile(sourcePath);
Enumeration<String> entries = dexfile.entries();
while (entries.hasMoreElements()) {
paths.add(entries.nextElement());
}
}
// Robolectric fallback
else {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources("");
while (resources.hasMoreElements()) {
String path = resources.nextElement().getFile();
if (path.contains("bin") || path.contains("classes")) {
paths.add(path);
}
}
}
for (String path : paths) {
File file = new File(path);
scanForModelClasses(file, packageName, context.getClassLoader());
}
}
private void scanForModelClasses(File path, String packageName,
ClassLoader classLoader) {
if (path.isDirectory()) {
for (File file : path.listFiles()) {
scanForModelClasses(file, packageName, classLoader);
}
} else {
String className = path.getName();
// Robolectric fallback
if (!path.getPath().equals(className)) {
className = path.getPath();
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
} else {
return;
}
className = className.replace(
System.getProperty("file.separator"), ".");
int packageNameIndex = className.lastIndexOf(packageName);
if (packageNameIndex < 0) {
return;
}
className = className.substring(packageNameIndex);
}
try {
Class<?> discoveredClass = Class.forName(className, false,
classLoader);
if (ReflectionUtils.isModel(discoveredClass)) {
@SuppressWarnings("unchecked")
Class<? extends Model> modelClass = (Class<? extends Model>) discoveredClass;
mTableInfos.put(modelClass, new TableInfo(modelClass));
} else if (ReflectionUtils.isTypeSerializer(discoveredClass)) {
TypeSerializer instance = (TypeSerializer) discoveredClass
.newInstance();
mTypeSerializers.put(instance.getDeserializedType(),
instance);
}
} catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
} catch (InstantiationException e) {
Log.e("Couldn't instantiate TypeSerializer.", e);
} catch (IllegalAccessException e) {
Log.e("IllegalAccessException", e);
}
}
}
}