/* * 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.util.Iterator; import java.util.HashMap; import org.hypergraphdb.HGHandle; import org.hypergraphdb.HGSearchResult; import org.hypergraphdb.HGSearchable; import org.hypergraphdb.HGIndex; import org.hypergraphdb.HGPersistentHandle; import org.hypergraphdb.HGException; import org.hypergraphdb.HyperGraph; import org.hypergraphdb.HGTypeSystem; import org.hypergraphdb.IncidenceSetRef; import org.hypergraphdb.LazyRef; import org.hypergraphdb.storage.BAtoBA; import org.hypergraphdb.storage.BAtoHandle; /** * <p> * The <code>SlotType</code> handles atoms of type <code>Slot</code>. Slots are used to * build up record types. * </p> * * <p> * <strong>IMPLEMENTATION NOTE:</strong> Eventually, it will be useful to be able to * search by slot name or slot value type only. To support this, it's probably best * to create a separate index for each component. Also, index management should * probably be updated once we have more flexible two-way indexes that represent * a 1-1 mapping (so that a key may be efficiently searched by the value of the index * entry); right now, removal of a slot is implemented by first constructing the Slot * instance...(which is ok as long as this removal is not performed very often). * </p> * @author Borislav Iordanov */ public final class SlotType implements HGCompositeType, HGSearchable<Slot, HGPersistentHandle> { public static final String INDEX_NAME = "hg_slot_value_index"; public static final String LABEL_DIMENSION = "label"; public static final String VALUE_TYPE_DIMENSION = "valueType"; private HyperGraph hg; private HGIndex<byte[], HGPersistentHandle> slotIndex = null; private byte [] slotToByteArray(Slot slot) { HGPersistentHandle valueType = hg.getPersistentHandle(slot.getValueType()); byte [] valueTypeAsBytes = valueType.toByteArray(); byte [] labelAsBytes = slot.getLabel().getBytes(); byte [] result = new byte[valueTypeAsBytes.length + labelAsBytes.length]; System.arraycopy(valueTypeAsBytes, 0, result, 0, valueTypeAsBytes.length); System.arraycopy(labelAsBytes, 0, result, valueTypeAsBytes.length, labelAsBytes.length); return result; } private HGIndex<byte[], HGPersistentHandle> getIndex() { if (slotIndex == null) { slotIndex = hg.getStore().getIndex(INDEX_NAME, BAtoBA.getInstance(), BAtoHandle.getInstance(hg.getHandleFactory()), null, true); } return slotIndex; } public void setHyperGraph(HyperGraph hg) { this.hg = hg; } public Object make(HGPersistentHandle handle, LazyRef<HGHandle[]> targetSet, IncidenceSetRef incidenceSet) { HGPersistentHandle [] layout = hg.getStore().getLink(handle); HGAtomType labelType = hg.getTypeSystem().getAtomType(String.class); String label = (String)labelType.make(layout[1], null, null); return new Slot(label, layout[0]); } public HGPersistentHandle store(Object instance) { Slot slot = (Slot)instance; HGTypeSystem typeSystem = hg.getTypeSystem(); HGPersistentHandle valueTypeHandle = hg.getPersistentHandle(slot.getValueType()); if (getIndex().findFirst(slotToByteArray(slot)) != null) throw new HGException("Attempting to create a duplicate slot: [name=" + slot.getLabel() + ", value type=" + valueTypeHandle); else { HGAtomType stringType = typeSystem.getAtomType(String.class); HGPersistentHandle labelHandle = stringType.store(slot.getLabel()); HGPersistentHandle handle = hg.getStore().store( new HGPersistentHandle[] { hg.getPersistentHandle(slot.getValueType()), labelHandle,}); getIndex().addEntry(slotToByteArray(slot), handle); return handle; } } public void release(HGPersistentHandle handle) { Object slot = make(handle, null, null); getIndex().removeAllEntries(slotToByteArray((Slot)slot)); hg.getStore().removeLink(handle); } public boolean subsumes(Object general, Object specific) { return false; } public HGSearchResult<HGPersistentHandle> find(Slot key) { byte [] asByteArray = slotToByteArray(key); return getIndex().find(asByteArray); } public Iterator<String> getDimensionNames() { if (projections == null) initProjections(); return projections.keySet().iterator(); } public HGProjection getProjection(String dimensionName) { if (projections == null) initProjections(); return projections.get(dimensionName); } private HashMap<String, HGProjection> projections = null; private synchronized void initProjections() { if (projections != null) return; projections = new HashMap<String, HGProjection>(); projections.put(LABEL_DIMENSION, new LabelProjection()); projections.put(VALUE_TYPE_DIMENSION, new ValueTypeProjection()); } private final class LabelProjection implements HGProjection { private final int [] layoutPath = new int[] {1}; public String getName() { return SlotType.LABEL_DIMENSION; } public HGHandle getType() { return hg.getTypeSystem().getTypeHandle(String.class); } public Object project(Object value) { return ((Slot)value).getLabel(); } public void inject(Object slot, Object label) { ((Slot)slot).setLabel((String)label); } public int [] getLayoutPath() { return layoutPath; } }; private final class ValueTypeProjection implements HGProjection { private int [] layoutPath = new int[] {0}; public String getName() { return SlotType.VALUE_TYPE_DIMENSION; } public HGHandle getType() { return hg.getTypeSystem().getTypeHandle(Top.class); } public Object project(Object value) { return ((Slot)value).getValueType(); } public void inject(Object slot, Object valueType) { ((Slot)slot).setValueType((HGHandle)valueType); } public int [] getLayoutPath() { return layoutPath; } }; }