/*
* 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 org.hypergraphdb.HGHandle;
import org.hypergraphdb.HGException;
import org.hypergraphdb.HGPersistentHandle;
import org.hypergraphdb.HGSearchResult;
import org.hypergraphdb.HyperGraph;
import org.hypergraphdb.IncidenceSetRef;
import org.hypergraphdb.LazyRef;
import org.hypergraphdb.HGQuery.hg;
import org.hypergraphdb.atom.AtomProjection;
import org.hypergraphdb.atom.HGAtomRef;
import org.hypergraphdb.util.HGUtils;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
/**
* <p>
* A <code>RecordType</code> represents a particular combination of slots that
* can be used to construct records. The latter are instances of <code>Record</code>.
* </p>
*
* <p>
* It is important that the slots in a record type are maintained in an order matching the HG store layout
* of record values.
* </p>
*
* <p>
* A null record is represented with a null handle {@link HGHandleFactory.nullHandle()}, while
* and empty record is represented with a newly created handle that doesn't point to anything.
* </p>
*
* @author Borislav Iordanov
*/
public class RecordType implements HGCompositeType
{
protected ArrayList<HGHandle> slots = new ArrayList<HGHandle>();
protected HyperGraph graph;
protected HGHandle thisHandle;
private HashMap<String, HGProjection> projections = null;
private HashMap<HGHandle, HGAtomRef.Mode> refModes = null;
private synchronized void initProjections()
{
if (projections != null)
return;
HashMap<String, HGProjection> tmp = new HashMap<String, HGProjection>();
for (int i = 0; i < slots.size(); i++)
{
Slot slot = (Slot)graph.get(slots.get(i));
tmp.put(slot.getLabel(), new SlotBasedProjection(slot, new int [] {i}));
}
projections = tmp;
}
public RecordType()
{
}
public synchronized HGAtomRef.Mode getReferenceMode(HGHandle slot)
{
if (refModes == null)
{
refModes = new HashMap<HGHandle, HGAtomRef.Mode>();
HGSearchResult<HGHandle> rs = null;
try
{
if (thisHandle == null)
thisHandle = graph.getHandle(this);
List<AtomProjection> L = hg.getAll(graph, hg.and(hg.type(AtomProjection.class),
hg.incident(thisHandle),
hg.orderedLink(thisHandle, graph.getHandleFactory().anyHandle())));
for (AtomProjection l : L)
{
HGHandle slotHandle = hg.findOne(graph, hg.eq(new Slot(l.getName(),l.getProjectionValueType())));
refModes.put(slotHandle, l.getMode());
}
}
catch (RuntimeException ex)
{
refModes = null;
//return null;
throw ex;
}
finally
{
HGUtils.closeNoException(rs);
}
}
return refModes.get(slot);
}
public void setThisHandle(HGHandle thisHandle)
{
this.thisHandle = thisHandle;
}
public Iterator<String> getDimensionNames()
{
if (projections == null)
initProjections();
return projections.keySet().iterator();
}
public HGProjection getProjection(String dimensionName)
{
if (projections == null)
initProjections();
return (HGProjection)projections.get(dimensionName);
}
public List<HGHandle> getSlots()
{
return slots;
}
public void addSlot(HGHandle slot)
{
if (!slots.contains(slot))
slots.add(slot);
}
public void remove(HGHandle slot)
{
slots.remove(slot);
}
public void removeAt(int i)
{
slots.remove(i);
}
public HGHandle getAt(int i)
{
return slots.get(i);
}
public int slotCount()
{
return slots.size();
}
public void setHyperGraph(HyperGraph hg)
{
this.graph = hg;
}
public Object make(HGPersistentHandle handle, LazyRef<HGHandle[]> targetSet, IncidenceSetRef incidenceSet)
{
if (graph.getHandleFactory().nullHandle().equals(handle))
return null;
Record result = null;
HGHandle [] targets = HGUtils.EMPTY_HANDLE_ARRAY;
if (targetSet != null)
{
targets = targetSet.deref();
if (targets == null)
targets = HGUtils.EMPTY_HANDLE_ARRAY;
}
if (targets.length > 0)
result = new LinkRecord(graph.getHandle(this), targets);
else
result = new Record(graph.getHandle(this));
TypeUtils.setValueFor(graph, handle, result);
HGPersistentHandle [] layout = slots.isEmpty() ?
HGUtils.EMPTY_HANDLE_ARRAY :
graph.getStore().getLink(handle);
if (layout.length != slots.size() * 2)
throw new HGException("RecordType.make: Record value of handle " +
handle +
" does not match record type number of slots.");
for (int i = 0; i < slots.size(); i++)
{
HGHandle slotHandle = getAt(i);
Object value = null;
// try
// {
if (!layout[2*i + 1].equals(graph.getHandleFactory().nullHandle()))
{
HGAtomRef.Mode refMode = getReferenceMode(slotHandle);
if (refMode != null)
{
AtomRefType refType = graph.getTypeSystem().getAtomType(HGAtomRef.class);
value = refType.make(layout[2*i + 1], null, null);
}
else
value = TypeUtils.makeValue(graph,
layout[2*i + 1],
graph.getTypeSystem().getType(layout[2*i]));
}
// }
// catch (HGException ex)
// {
// Slot s = graph.get(slotHandle);
// System.err.println("Unable to get value for slot: " + s.getLabel());
// throw ex;
// }
result.set((Slot)graph.get(slotHandle), value);
}
return result;
}
public HGPersistentHandle store(Object instance)
{
if (instance == null)
return graph.getHandleFactory().nullHandle();
HGPersistentHandle handle = TypeUtils.getNewHandleFor(graph, instance);
if (slots.isEmpty())
return handle;
if (! (instance instanceof Record))
throw new HGException("RecordType.store: object is not of type Record.");
Record record = (Record)instance;
HGPersistentHandle [] layout = new HGPersistentHandle[slots.size() * 2];
for (int i = 0; i < slots.size(); i++)
{
HGHandle slotHandle = getAt(i);
Slot slot = (Slot)graph.get(slotHandle);
Object value = record.get(slot);
if (value == null)
{
layout[2*i] = graph.getPersistentHandle(slot.getValueType());
layout[2*i + 1] = graph.getHandleFactory().nullHandle();
}
else
{
HGAtomRef.Mode refMode = getReferenceMode(slotHandle);
if (refMode == null)
{
HGHandle actualTypeHandle = graph.getTypeSystem().getTypeHandle(value.getClass());
if (actualTypeHandle == null)
actualTypeHandle = slot.getValueType();
else if (actualTypeHandle.equals(graph.getTypeSystem().getTop()))
throw new HGException("Got TOP type for value for Java class " + value.getClass());
HGAtomType type = graph.getTypeSystem().getType(actualTypeHandle);
layout[2*i] = graph.getPersistentHandle(actualTypeHandle);
try
{
layout[2*i + 1] = TypeUtils.storeValue(graph, value, type);
}
catch (HGException ex)
{
throw new HGException("Failed on slot '" + slot.getLabel() + "' of class " + value.getClass(),ex);
}
}
else
{
layout[2*i] = graph.getPersistentHandle(slot.getValueType());
if (value instanceof HGAtomRef)
{
AtomRefType refType = graph.getTypeSystem().getAtomType(HGAtomRef.class);
layout[2*i + 1] = refType.store((HGAtomRef)value);
}
else
throw new HGException("Slot " + slot.getLabel() +
" should have an atom reference for record " +
graph.getHandle(this));
}
}
}
graph.getStore().store(handle, layout);
return handle;
}
public void release(HGPersistentHandle handle)
{
if (slots.isEmpty() || graph.getHandleFactory().nullHandle().equals(handle))
return;
HGPersistentHandle [] layout = graph.getStore().getLink(handle);
if (layout == null)
// this is fishy, a sys print out like this, next line will throw an NPE anyway
System.out.println("oops, no data for : " + handle);
if (layout.length != slots.size() * 2)
throw new HGException("RecordType.remove: Record value of handle " +
handle +
" does not match record type number of slots.");
for (int i = 0; i < slots.size(); i++)
{
HGHandle slotHandle = (HGHandle)getAt(i);
HGAtomType type;
if (getReferenceMode(slotHandle) == null)
type = graph.getTypeSystem().getType(layout[2*i]);
else
type = graph.getTypeSystem().getAtomType(HGAtomRef.class);
int j = 2*i + 1;
if (!layout[j].equals(graph.getHandleFactory().nullHandle()))
if (!TypeUtils.isValueReleased(graph, layout[j]))
{
TypeUtils.releaseValue(graph, type, layout[j]);
}
}
graph.getStore().removeLink(handle);
}
public boolean subsumes(Object general, Object specific)
{
return false;
}
public boolean equals(Object other)
{
if (! (other instanceof RecordType))
return false;
else
{
RecordType otherR = (RecordType)other;
if(slots.size() != otherR.slots.size())
return false;
for(int i =0; i < slots.size(); i++)
if(!slots.get(i).equals(otherR.slots.get(i)))
return false;
return true;
}
}
public String toString()
{
String res = "";
for(int i =0; i < slots.size(); i++)
{
String n = ((Slot)graph.get(getAt(i))).getValueType().getClass().getName();
res += n.substring(n.lastIndexOf('.') + 1);
if(i != slots.size() -1)
res += "/";
}
return res;
}
}