/* * 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.Constructor; import java.util.Iterator; import org.hypergraphdb.HGHandle; import org.hypergraphdb.HGException; import org.hypergraphdb.HGLink; import org.hypergraphdb.HGPersistentHandle; import org.hypergraphdb.IncidenceSetRef; import org.hypergraphdb.LazyRef; import org.hypergraphdb.atom.HGAtomRef; import org.hypergraphdb.indexing.CompositeIndexer; import org.hypergraphdb.indexing.HGIndexer; import org.hypergraphdb.indexing.HGKeyIndexer; /** * <p> * Acts as a <code>HGAtomType</code> for Java beans. Uses underlying * <code>RecordType</code> instance to manage the layout in the store, but * operates on actual bean instances instead of <code>Record</code>s. * </p> * * @author Borislav Iordanov */ public class JavaBeanBinding extends JavaAbstractBinding { private Constructor<?> defaultConstructor = null; private Constructor<?> linkConstructor = null; private Constructor<?> handleListConstructor = null; private boolean isLinkInstance; // save the value of HGLink.isAssignableFrom(javaClass) - expensive to compute. public JavaBeanBinding(HGHandle typeHandle, RecordType hgType, Class<?> clazz) { super(typeHandle, hgType, clazz); defaultConstructor = JavaTypeFactory.findDefaultConstructor(javaClass); if (defaultConstructor != null) defaultConstructor.setAccessible(true); try { linkConstructor = javaClass.getDeclaredConstructor(new Class[] {HGHandle[].class} ); if (linkConstructor != null) linkConstructor.setAccessible(true); } catch (NoSuchMethodException ex) { handleListConstructor = JavaTypeFactory.findHandleArgsConstructor(javaClass); if (handleListConstructor != null) handleListConstructor.setAccessible(true); } isLinkInstance = HGLink.class.isAssignableFrom(javaClass); } private HGLink makeLink(HGHandle[] targetSet) throws Exception { if (linkConstructor != null) return (HGLink)linkConstructor.newInstance(new Object[]{targetSet}); else if (handleListConstructor != null) { return (HGLink)handleListConstructor.newInstance((Object[])targetSet); } else throw new RuntimeException("Can't construct link with Java type " + javaClass.getName() + " please include a (HGHandle [] ) " + " or a (HGHandle, HGHandle, ..., HGHandle) constructor."); } public Object make(HGPersistentHandle handle, LazyRef<HGHandle[]> targetSet, IncidenceSetRef incidenceSet) { // if (this.javaClass.getName().equals(CompositeIndexer.class.getName())) // { // RecordType recordType = (RecordType)hgType; // for (Iterator<HGHandle> i = recordType.getSlots().iterator(); i.hasNext(); ) // { // HGHandle slotHandle = i.next(); // Slot slot = graph.get(slotHandle); // if (slot.getLabel().equals("indexerParts")) // { // HGHandle slotType = graph.getTypeSystem().getTypeHandle(HGKeyIndexer[].class); // if (!slotType.equals(slot.getValueType())) // { // slot.setValueType(slotType); // graph.update(slot); // } // } // } // } Object bean = null; try { // We construct a link of the class is a HGLink and the target set is not-empty // or if we don't have a default constructor at all. if (isLinkInstance && targetSet != null && targetSet.deref().length > 0 || defaultConstructor == null) bean = makeLink(targetSet.deref()); else if (defaultConstructor != null) bean = defaultConstructor.newInstance(); else throw new RuntimeException("Can't construct object of type " + javaClass.getName() + " no default constructor and/or no HGHandle array-based constructor."); TypeUtils.setValueFor(graph, handle, bean); Record record = (Record)hgType.make(handle, targetSet, null); RecordType recordType = (RecordType)hgType; for (HGHandle slotHandle : recordType.getSlots()) { Slot slot = (Slot)graph.get(slotHandle); Object value = record.get(slot); if (value != null && recordType.getReferenceMode(slotHandle) != null) value = graph.get(((HGAtomRef)value).getReferent()); try { // TODO: tmp for porting old DBs to new version. // DELME // if (value instanceof HGIndexer[]) // { // HGIndexer [] A = (HGIndexer[])value; // HGKeyIndexer [] newvalue = new HGKeyIndexer[A.length]; // for (int i = 0; i < A.length; i++) newvalue[i] = (HGKeyIndexer)A[i]; // value = newvalue; // } BonesOfBeans.setProperty(bean, slot.getLabel(), value); } catch (Throwable t) { throw new HGException("Failed to assign property: " + slot.getLabel() + " to bean " + bean.getClass(), t); } } } catch (InstantiationException ex) { throw new HGException("Unable to instantiate bean of type '" + javaClass.getName() + "', make sure that bean has a default constructor declared.", ex); } catch (HGException t) { throw t; } catch (Throwable t) { throw new HGException("JavaTypeBinding.make: " + t.toString(), t); } return bean; } public HGPersistentHandle store(final Object instance) { HGPersistentHandle result = TypeUtils.getHandleFor(graph, instance); if (result == null) { final Record record = new BeanRecord(typeHandle, instance); RecordType recordType = (RecordType)hgType; for (HGHandle slotHandle : recordType.getSlots()) { Slot slot = (Slot)graph.get(slotHandle); Object value = BonesOfBeans.getProperty(instance, slot.getLabel()); HGAtomRef.Mode refMode = recordType.getReferenceMode(slotHandle); if (refMode != null && value != null) { HGHandle valueAtomHandle = graph.getHandle(value); if (valueAtomHandle == null) { HGAtomType valueType = (HGAtomType)graph.get(slot.getValueType()); valueAtomHandle = graph.getPersistentHandle( graph.add(value, valueType instanceof HGAbstractType ? graph.getTypeSystem().getTypeHandle(value.getClass()) : slot.getValueType())); } value = new HGAtomRef(valueAtomHandle, refMode); } record.set(slot, value); } result = hgType.store(record); } return result; } public void release(HGPersistentHandle handle) { // System.out.println("Release " + handle + " of type " + this.getJavaClass().getName()); hgType.release(handle); } static class BeanRecord extends Record implements TypeUtils.WrappedRuntimeInstance { Object bean; BeanRecord(HGHandle h, Object bean) { super(h); this.bean = bean;} public Object getRealInstance() { return bean; } } }