package org.zstack.configuration;
import org.apache.commons.io.FileUtils;
import org.zstack.header.configuration.APIGenerateSqlForeignKeyMsg;
import org.zstack.header.configuration.APIGenerateSqlIndexMsg;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.vo.EO;
import org.zstack.header.vo.Index;
import org.zstack.utils.BeanUtils;
import org.zstack.utils.FieldUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import org.zstack.utils.path.PathUtil;
import javax.persistence.Entity;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
/**
*/
public class SqlIndexGenerator {
private static CLogger logger = Utils.getLogger(SqlIndexGenerator.class);
private class IndexInfo {
Class entity;
Field indexField;
IndexInfo(Class entity, Field f) {
this.entity = entity;
indexField = f;
}
String toIndexSql() {
Index idx = indexField.getAnnotation(Index.class);
if (String.class.isAssignableFrom(indexField.getType()) && idx.length() != -1) {
return String.format("CREATE INDEX %s ON %s (%s(%s));",
String.format("idx%s%s", entity.getSimpleName(), indexField.getName()),
entity.getSimpleName(),
indexField.getName(),
idx.length()
);
} else {
return String.format("CREATE INDEX %s ON %s (%s);",
String.format("idx%s%s", entity.getSimpleName(), indexField.getName()),
entity.getSimpleName(),
indexField.getName()
);
}
}
}
private String outputPath;
private List<String> basePkgs;
private List<Class> entityClass = new ArrayList<Class>();
private Map<Class, List<IndexInfo>> indexMap = new HashMap<Class, List<IndexInfo>>();
private StringBuilder writer = new StringBuilder();
public SqlIndexGenerator(APIGenerateSqlIndexMsg msg) {
outputPath = msg.getOutputPath();
if (outputPath == null) {
outputPath = PathUtil.join(System.getProperty("user.home"), "zstack-sql", "indexes.sql");
}
basePkgs = msg.getBasePackageNames();
if (basePkgs == null) {
basePkgs = Arrays.asList("org.zstack");
}
}
public void generate() {
for (String pkgName: basePkgs) {
entityClass.addAll(BeanUtils.scanClass(pkgName, Entity.class));
}
for (Class entity : entityClass) {
collectIndex(entity);
}
generateIndex();
}
private void generateIndex() {
List<Class> classes = new ArrayList<Class>();
classes.addAll(indexMap.keySet());
Collections.sort(classes, new Comparator<Class>() {
@Override
public int compare(Class o1, Class o2) {
return o1.getSimpleName().compareTo(o2.getSimpleName());
}
});
for (Class clz : classes) {
generateIndexForEntity(indexMap.get(clz));
}
try {
FileUtils.writeStringToFile(new File(outputPath), writer.toString());
} catch (IOException e) {
throw new CloudRuntimeException(e);
}
}
private void generateIndexForEntity(List<IndexInfo> keys) {
if (keys.isEmpty()) {
return;
}
writer.append(String.format("\n# Index for table %s\n", keys.get(0).entity.getSimpleName()));
for (IndexInfo key : keys) {
writer.append(String.format("\n%s", key.toIndexSql()));
}
writer.append("\n");
}
private void collectIndex(Class entity) {
List<Field> fs;
Class superClass = entity.getSuperclass();
if (superClass.isAnnotationPresent(Entity.class) || entity.isAnnotationPresent(EO.class)) {
// parent class or EO class is also an entity, it will take care of its foreign key,
// so we only do our own foreign keys;
fs = FieldUtils.getAnnotatedFieldsOnThisClass(Index.class, entity);
} else {
fs = FieldUtils.getAnnotatedFields(Index.class, entity);
}
List<IndexInfo> keyInfos = indexMap.get(entity);
if (keyInfos == null) {
keyInfos = new ArrayList<IndexInfo>();
indexMap.put(entity, keyInfos);
}
for (Field f : fs) {
keyInfos.add(new IndexInfo(entity, f));
}
}
}