package jef.database.meta;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import jef.database.DbCfg;
import jef.database.annotation.EasyEntity;
import jef.database.meta.AnnotationProvider.ClassAnnotationProvider;
import jef.database.meta.AnnotationProvider.FieldAnnotationProvider;
import jef.tools.Assert;
import jef.tools.JefConfiguration;
import jef.tools.ResourceUtils;
import jef.tools.StringUtils;
import jef.tools.XMLUtils;
import jef.tools.reflect.BeanUtils;
import jef.tools.reflect.Enums;
import jef.tools.resource.IResource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
/**
* 默认的元模型加载工具
*
* @author jiyi
*
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class DefaultMetaLoader implements MetadataConfiguration {
private Map<String, URL> urlmaps;
private static final Id ID = BeanUtils.asAnnotation(Id.class, Collections.EMPTY_MAP);
private static final Entity ENTITY = BeanUtils.asAnnotation(Entity.class, new HashMap<String, Object>(2));
private String pattern;
private int patternType = 0;
static DefaultMetaLoader instance;
public DefaultMetaLoader() {
String pattern = StringUtils.trimToNull(JefConfiguration.get(DbCfg.METADATA_RESOURCE_PATTERN));
setPattern(pattern);
instance = this;
}
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
if (StringUtils.equals(pattern, this.pattern)) {
return;
}
if (pattern != null) {
if (pattern.indexOf("%s") > -1) {
patternType = 1;
} else if (pattern.indexOf("%c") > -1) {
patternType = 2;
} else if (pattern.indexOf("%*") > -1) {
patternType = 3;
}
}
this.pattern = pattern;
urlmaps = null;
}
private Container loadFromResourceUrl(URL url, Class<?> cls) throws SAXException, IOException {
Document doc = XMLUtils.loadDocument(url);
Element entityNode = findEntityNode(doc, cls);
if (entityNode == null) {
return null;
// throw new
// IllegalArgumentException("No definition found for class [" + cls
// + "] in " + url);
}
Container c = new Container(cls);
Map t = XMLUtils.getAttributesMap(entityNode);
if ("class".equals(entityNode.getNodeName())) {
t.put("class", t.get("name"));
t.put("name", t.get("table"));
}
c.table = BeanUtils.asAnnotation(Table.class, t);
if (t.get("refersh") != null || t.get("checkEnhanced") != null) {
adjustType("refersh", t, true);
adjustType("checkEnhanced", t, true);
c.easy = BeanUtils.asAnnotation(EasyEntity.class, t);
}
for (Element e : XMLUtils.childElements(entityNode, "property", "id")) {
String name = e.getAttribute("name");
if (StringUtils.isEmpty(name)) {
continue;
}
Map<Class<?>, Annotation> cas = new HashMap<Class<?>, Annotation>();
if ("id".equals(e.getNodeName())) {
cas.put(Id.class, ID);
}
Element column = XMLUtils.first(e, "column");
if (column != null) {
Map map = XMLUtils.getAttributesMap(column, true);
adjustType("unique", map, false);
adjustType("nullable", map, true);
adjustType("insertable", map, true);
adjustType("updatable", map, true);
adjustType("length", map, 255);
adjustType("precision", map, 0);
adjustType("scale", map, 0);
adjustType("name", map, "");
adjustType("columnDefinition", map, "");
adjustType("table", map, "");
cas.put(javax.persistence.Column.class, BeanUtils.asAnnotation(javax.persistence.Column.class, map));
}
Element generator = XMLUtils.first(e, "generator");
if (generator != null) {
Map map = XMLUtils.getAttributesMap(generator);
String gClass = (String) map.get("class");
if (StringUtils.isNotEmpty(gClass)) {
if ("identity".equalsIgnoreCase(gClass)) {
map.put("strategy", GenerationType.IDENTITY);
} else if ("sequence".equalsIgnoreCase(gClass)) {
map.put("strategy", GenerationType.SEQUENCE);
}
}
String sequence = getChildParamText(generator, "sequence");
if (StringUtils.isNotEmpty(sequence)) {
Map<String, Object> newMap = new HashMap<String, Object>();
newMap.put("name", sequence);
newMap.put("sequenceName", sequence);
cas.put(SequenceGenerator.class, BeanUtils.asAnnotation(SequenceGenerator.class, newMap));
}
adjustType("strategy", map, GenerationType.AUTO);
cas.put(GeneratedValue.class, BeanUtils.asAnnotation(GeneratedValue.class, map));
}
c.fieldColumns.put(name, cas);
}
return c;
}
private String getChildParamText(Element generator, String key) {
for (Element x1 : XMLUtils.childElements(generator, "param")) {
String tmpKey = x1.getAttribute("name");
if (tmpKey.equals(key)) {
return XMLUtils.nodeText(x1);
}
}
return null;
}
private Element findEntityNode(Document doc, Class<?> cls) {
Element root = doc.getDocumentElement();
String rootNodeName = root.getNodeName();
if ("class".equals(rootNodeName) || "table".equals(rootNodeName)) {
return root;
}
List<Element> children = XMLUtils.childElements(root, "table", "class");
for (Element ele : children) {
if ("class".equals(ele.getNodeName())) {
String clz = ele.getAttribute("name");
if (StringUtils.equals(cls.getName(), clz)) {
return ele;
}
} else {
if (children.size() == 1) {
return ele;
}
String clz = ele.getAttribute("class");
if (StringUtils.equals(cls.getName(), clz)) {
return ele;
}
}
}
return null;
}
private <T extends Enum<T>> void adjustType(String string, Map map, Enum<T> enumValue) {
Object value = map.get(string);
if (value instanceof String) {
String text = (String) value;
value = null;
if (StringUtils.isEmpty(text)) {
value = enumValue;
} else {
value = Enums.valueOf(enumValue.getDeclaringClass(), text.toUpperCase(), (T) enumValue);
}
map.put(string, value);
} else if (value == null) {
map.put(string, enumValue);
}
}
private void adjustType(String string, Map map, String value) {
Object v = map.get(string);
if (v == null) {
map.put(string, value);
}
}
private void adjustType(String string, Map map, int i) {
Object value = map.get(string);
if (value instanceof String) {
String text = (String) value;
value = null;
if (StringUtils.isEmpty(text)) {
value = i;
} else {
value = StringUtils.toInt(text, i);
}
map.put(string, value);
} else if (value == null) {
map.put(string, i);
}
}
private void adjustType(String string, Map map, boolean b) {
Object value = map.get(string);
if (value instanceof String) {
String text = (String) value;
value = null;
if (StringUtils.isEmpty(text)) {
value = b;
} else {
value = StringUtils.toBoolean(text, b);
}
map.put(string, value);
} else if (value == null) {
map.put(string, b);
}
}
protected URL getResource(Class<?> clz) {
if (pattern == null) {
return null;
}
switch (patternType) {
case 0:
return clz.getResource(pattern);
case 1:
return clz.getResource(pattern.replace("%s", clz.getSimpleName()));
case 2:
return clz.getResource(pattern.replace("%c", clz.getName()));
case 3:
if (urlmaps == null) {
initUrlMap();
}
return urlmaps.get(clz.getName());
}
return null;
}
private void initUrlMap() {
try {
urlmaps = new HashMap<String, URL>();
IResource[] resources = ResourceUtils.findResources(pattern);
for (IResource res : resources) {
parseXmlClass(res);
}
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
private synchronized void parseXmlClass(IResource res) throws SAXException, IOException {
Document doc = XMLUtils.loadDocument(res.getURL());
Element root = doc.getDocumentElement();
for (Element ele : XMLUtils.childElements(root, "class")) {
String name = ele.getAttribute("name");
if (StringUtils.isNotEmpty(name)) {
urlmaps.put(name, res.getURL());
}
}
for (Element ele : XMLUtils.childElements(root, "table")) {
String name = ele.getAttribute("class");
if (StringUtils.isNotEmpty(name)) {
urlmaps.put(name, res.getURL());
}
}
}
public ClassAnnotationProvider getAnnotations(Class clz) {
URL url = getResource(clz);
if (url != null) {
try {
ClassAnnotationProvider ano = loadFromResourceUrl(url, clz);
if (ano != null)
return ano;
} catch (SAXException e) {
throw new IllegalArgumentException(e);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
return new AnnoImpl(clz);
}
static class AnnoImpl implements ClassAnnotationProvider {
private Class<?> clz;
public AnnoImpl(Class clz) {
this.clz = clz;
}
public <T extends Annotation> T getAnnotation(Class<T> type) {
return clz.getAnnotation(type);
}
public <T extends Annotation> T getFieldAnnotation(Field fieldname, Class<T> type) {
if (fieldname == null) {
return null;
}
return fieldname.getAnnotation(type);
}
@Override
public String getName() {
return clz.getName();
}
@Override
public FieldAnnotationProvider forField(Field field) {
return new FieldProviderImpl(field);
}
}
static class FieldProviderImpl implements FieldAnnotationProvider {
protected java.lang.reflect.Field field;
public FieldProviderImpl(Field field) {
Assert.notNull(field);
this.field = field;
}
@Override
public <T extends Annotation> T getAnnotation(Class<T> type) {
return field.getAnnotation(type);
}
@Override
public String getName() {
return field.getName();
}
@Override
public Class<?> getDeclaringClass() {
return field.getDeclaringClass();
}
@Override
public Type getGenericType() {
return field.getGenericType();
}
@Override
public Class<?> getType() {
return field.getType();
}
}
private static class Container implements ClassAnnotationProvider {
private Class<?> clz;
private EasyEntity easy;
private String type;
private Table table;
private Map<String, Map<Class<?>, Annotation>> fieldColumns = new HashMap<String, Map<Class<?>, Annotation>>();
public Container(Class<?> cls) {
clz = cls;
}
public String getName() {
return type;
}
public <T extends Annotation> T getAnnotation(Class<T> type) {
T t = null;
if (type == Table.class) {
t = (T) table;
} else if (type == EasyEntity.class) {
t = (T) easy;
} else if (type == Entity.class) {
return (T) ENTITY;
}
return t == null ? clz.getAnnotation(type) : t;
}
@Override
public FieldAnnotationProvider forField(Field field) {
return new FieldProviderImpl(field) {
@Override
public <T extends Annotation> T getAnnotation(Class<T> type) {
Map<Class<?>, Annotation> anns = fieldColumns.get(field.getName());
T t = null;
if (anns != null) {
t = (T) anns.get(type);
}
return t == null ? field.getAnnotation(type) : t;
}
};
}
}
}