/*
* This file is part of the HyperGraphDB source distribution. This is copyrighted
* software. For permitted uses, licensing options and redistribution, please see
* the LicensingInformation file at the root level of the distribution.
*
* Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved.
*/
package org.hypergraphdb.type;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import org.hypergraphdb.HGException;
import org.hypergraphdb.HGHandle;
import org.hypergraphdb.HGIndex;
import org.hypergraphdb.HGPersistentHandle;
import org.hypergraphdb.HGSearchResult;
import org.hypergraphdb.HGTypeSystem;
import org.hypergraphdb.HyperGraph;
import org.hypergraphdb.annotation.AtomReference;
import org.hypergraphdb.annotation.HGIgnore;
import org.hypergraphdb.atom.AtomProjection;
import org.hypergraphdb.atom.HGAtomRef;
import org.hypergraphdb.atom.HGSerializable;
import org.hypergraphdb.indexing.ByPartIndexer;
import org.hypergraphdb.indexing.HGIndexer;
import org.hypergraphdb.util.HGUtils;
public class JavaObjectMapper implements JavaTypeMapper
{
protected HyperGraph graph = null;
protected HashSet<String> classes = null;
protected HGHandle superSlot = null;
protected HGIndex<String, HGPersistentHandle> idx = null;
protected HGIndex<String, HGPersistentHandle> getIndex()
{
if (idx == null)
{
HGHandle t = graph.getTypeSystem().getTypeHandle(HGSerializable.class);
HGIndexer indexer = new ByPartIndexer(t, "classname");
idx = graph.getIndexManager().getIndex(indexer);
if (idx == null)
{
idx = graph.getIndexManager().register(indexer);
}
return idx;
}
return idx;
}
public HGHandle getSuperSlot()
{
return JavaTypeFactory.getSuperSlot(graph);
}
protected void initClasses()
{
if (classes != null)
return;
classes = new HashSet<String>();
HGIndex<String, HGPersistentHandle> idx = getIndex();
HGSearchResult<String> rs = idx.scanKeys();
try
{
while (rs.hasNext())
classes.add(rs.next());
}
catch (Exception ex)
{
throw new HGException(ex);
}
finally
{
HGUtils.closeNoException(rs);
}
}
protected boolean checkClass(Class<?> javaClass)
{
if (!classes.contains(javaClass.getName()))
{
Class<?> parent = javaClass.getSuperclass();
if (parent == null)
return false;
if (checkClass(parent))
return true;
for (Class<?> in : javaClass.getInterfaces())
if (checkClass(in))
return true;
return false;
}
else
return true;
}
protected boolean mapAsSerializableObject(Class<?> javaClass)
{
initClasses();
return checkClass(javaClass);
}
private boolean ignoreField(Field f)
{
int m = f.getModifiers();
return (m & Modifier.TRANSIENT) != 0 ||
(m & Modifier.STATIC) != 0 ||
f.getAnnotation(HGIgnore.class) != null;
}
private HGAtomRef.Mode getReferenceMode(Class<?> javaClass, Field field)
{
//
// Retrieve or recursively create a new type for the nested
// bean.
//
AtomReference ann = (AtomReference)field.getAnnotation(AtomReference.class);
if (ann == null)
return null;
String s = ann.value();
if ("hard".equals(s))
return HGAtomRef.Mode.hard;
else if ("symbolic".equals(s))
return HGAtomRef.Mode.symbolic;
else if ("floating".equals(s))
return HGAtomRef.Mode.floating;
else
throw new HGException("Wrong annotation value '" + s +
"' for field '" + field.getName() + "' of class '" +
javaClass.getName() + "', must be one of \"hard\", \"symbolic\" or \"floating\".");
}
public void setHyperGraph(HyperGraph graph)
{
this.graph = graph;
}
public HGAtomType defineHGType(Class<?> javaClass, HGHandle typeHandle)
{
if (!mapAsSerializableObject(javaClass))
return null;
HGTypeSystem typeSystem = graph.getTypeSystem();
// JavaTypeMapper javaTypes = typeSystem.getJavaTypeFactory();
Field fields [] = javaClass.getDeclaredFields();
RecordType recType = new RecordType();
// First, find out about the super slot which we put if the parent is a
// non-empty record.
if (javaClass.getSuperclass() != null)
{
// Make sure we handle properly recursive calls - if the type handle yields
// a 'Class' instance, it means we're in the process of constructing the type.
// So we can simply check whether it has any fields that are not to be ignored.
boolean has_parent = false;
HGHandle parentTypeHandle = typeSystem.getTypeHandle(javaClass.getSuperclass());
Object x = graph.get(parentTypeHandle);
if (x instanceof Class<?>)
{
Class<?> clazz = (Class<?>)x;
for (Field pf : clazz.getDeclaredFields())
if (!ignoreField(pf)) { has_parent = true; break; }
}
else
{
HGAtomType parentType = typeSystem.getAtomType(javaClass.getSuperclass());
has_parent = parentType instanceof HGCompositeType &&
((HGCompositeType)parentType).getDimensionNames().hasNext();
}
if (has_parent)
recType.addSlot(getSuperSlot());
}
for (Field field : fields)
{
if (ignoreField(field))
continue;
HGHandle fieldTypeHandle = typeSystem.getTypeHandle(field.getType());
if (fieldTypeHandle == null)
{
throw new HGException("Unable to create HG type for field " +
field.getName() + " of class " + javaClass.getName());
}
HGHandle slotHandle = JavaTypeFactory.getSlotHandle(graph, field.getName(), fieldTypeHandle);
Slot slot = graph.get(slotHandle);
recType.addSlot(slotHandle);
HGAtomRef.Mode refMode = getReferenceMode(javaClass, field);
if (refMode != null)
typeSystem.getHyperGraph().add(new AtomProjection(typeHandle,
slot.getLabel(),
slot.getValueType(),
refMode));
}
if (recType.getSlots().isEmpty())
return new HGAbstractType();
// else if (!isInstantiable(javaTypes, javaClass))
// return defineComposite(typeSystem, fields);
else
return recType;
}
public HGAtomType getJavaBinding(HGHandle typeHandle,
HGAtomType hgType,
Class<?> javaClass)
{
if (mapAsSerializableObject(javaClass))
{
if (hgType instanceof RecordType)
{
RecordType recType = (RecordType)hgType;
recType.setThisHandle(typeHandle);
if (JavaTypeFactory.isHGInstantiable(javaClass))
return new JavaObjectBinding(typeHandle, recType, javaClass);
else
return new JavaAbstractBinding(typeHandle, recType, javaClass);
}
else if (hgType instanceof HGCompositeType)
return new JavaAbstractBinding(typeHandle, (HGCompositeType)hgType, javaClass);
else if (hgType instanceof HGAbstractType)
return new JavaInterfaceBinding(typeHandle, hgType, javaClass);
else
return hgType;
}
return null;
}
public void addClass(Class<?> c)
{
addClass(c.getName());
}
public void addClass(String classname)
{
initClasses();
try
{
Class<?> c = Class.forName(classname);
for (String existing : classes)
{
Class<?> e = null;
try { e = Class.forName(existing); }
catch (Exception ex) { }
if (e != null && e.isAssignableFrom(c))
return;
}
graph.add(new HGSerializable(classname));
classes.add(classname);
}
catch (Exception ex)
{
throw new HGException(ex);
}
}
}