/*
Copyright (C) 2007 Mobixess Inc. http://www.java-objects-database.com
This file is part of the JODB (Java Objects Database) open source project.
JODB is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation.
JODB is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.mobixess.jodb.core.index;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Vector;
import com.mobixess.jodb.core.IllegalClassTypeException;
import com.mobixess.jodb.core.agent.JODBAgent;
import com.mobixess.jodb.core.io.IOBase;
import com.mobixess.jodb.core.io.JODBOperationContext;
import com.mobixess.jodb.core.io.ObjectDataContainer;
import com.mobixess.jodb.core.io.ObjectDataContainer.FIELD_CATEGORIES;
import com.mobixess.jodb.core.io.ObjectDataContainer.FieldRecord;
import com.mobixess.jodb.core.io.ObjectDataContainer.FieldsIterator;
import com.mobixess.jodb.core.transaction.TransactionContainer;
import com.mobixess.jodb.core.transaction.TransactionUtils;
import com.mobixess.jodb.core.transaction.TransactionUtils.DataContainersCache;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil;
public class JODBIndexingRootAgent extends JODBAgent {
private final static int DEFAULT_CAPACITY = 30;
private final static int DEFAULT_INCREMENT = 30;
private JODBIndexingAgent[] _agents;
private short _totalAgents;
private int _capacityIncrement;
private long _version;
public JODBIndexingRootAgent() {
this(DEFAULT_CAPACITY, DEFAULT_INCREMENT);
}
public JODBIndexingRootAgent(int initialCapacity, int capacityIncrement) {
_agents = new JODBIndexingAgent[initialCapacity];
_capacityIncrement = capacityIncrement;
}
public void setVersion(long version) {
_version = version;
}
public long getVersion() {
return _version;
}
public synchronized JODBIndexingAgent enableIndex(Field field, JODBOperationContext context) throws IOException{
TransactionContainer transactionContainer = context.getTransactionContainer();
transactionContainer.enableAgentMode();
try{
JODBIndexingAgent agent = addAgent(field, context);
IOBase base = context.getBase();
int classId = base.getOrSetClassTypeSubstitutionID(field.getDeclaringClass());
int fieldId = base.getOrSetFieldSubstitutionID(field);
long[] allOffsets = base.getForAllObjects(context.getIoTicket());
DataContainersCache dataContainersCache = TransactionUtils.getObjectDataContainerCache();
ObjectDataContainer dataContainer = dataContainersCache.pullObjectDataContainer();
try{
for (int i = 0; i < allOffsets.length; i++) {
processIndexForObjectId(allOffsets[i], classId, fieldId, field.getType(), dataContainer, agent, context);
dataContainer.reset();
}
try {
transactionContainer.set(agent, Integer.MAX_VALUE);
} catch (IllegalClassTypeException e) {
e.printStackTrace();
}
}finally{
dataContainersCache.pushObjectDataContainer(dataContainer);
}
return agent;
}finally{
transactionContainer.disableAgentMode();
}
}
private void processIndexForObjectId(long objectOffset, int classId, int fieldId, Class fieldType, ObjectDataContainer dataContainer, JODBIndexingAgent agent, JODBOperationContext context) throws IOException{
FieldsIterator fieldsIterator = dataContainer.readObject(context, objectOffset, true);
if(fieldsIterator!=null){
if(dataContainer.getOriginalClassType()!=classId){
return;
}
if(dataContainer.getOriginalClassType() != dataContainer.getTranslatedClassType()){
throw new IllegalArgumentException("No indexing for translated classes");
}
FieldRecord fieldRecord = dataContainer.getRecordCache();
IOBase base = context.getBase();
while (fieldsIterator.hasNext()) {
fieldsIterator.next(fieldRecord, base,false);
if(fieldRecord._category != FIELD_CATEGORIES.PRIMITIVE){//TODO optimize to skip none primitive fields
continue;
}
if(fieldRecord._fieldID == fieldId){
agent.insertIndex(objectOffset, fieldRecord._primitiveRawDataBuffer, context);
return;
}
}
//add field as having default value
ByteBuffer defaultValue = PrimitiveJavaTypesUtil.getDefaultValueAsByteBuffer(fieldType);
agent.insertIndex(objectOffset, defaultValue, context);
}
}
public synchronized void getAgentsForClassId(Vector<IndexingRecord> agentsBuffer, int classTypeId){
for (int i = 0; i < _totalAgents; i++) {
if(_agents[i].getClassId() == classTypeId){
IndexingRecord indexingRecord = TransactionUtils.getObjectDataContainerCache().pullIndexRecord();
indexingRecord.setIndexingAgent(_agents[i]);
agentsBuffer.add(indexingRecord);
}
}
}
private synchronized JODBIndexingAgent addAgent(Field field, JODBOperationContext context) throws IOException{
TransactionContainer transactionContainer = context.getTransactionContainer();
transactionContainer.enableAgentMode();
try{
int fieldId = context.getBase().getOrSetFieldSubstitutionID(field);
for (int i = 0; i < _totalAgents; i++) {
if(_agents[i].getFieldId() == fieldId){
return _agents[i];
}
}
_totalAgents++;
_agents = ensurePersistentArrayCapacity(_agents, _totalAgents, transactionContainer, _capacityIncrement);
_agents[_totalAgents-1] = new JODBIndexingAgent(field,context);
try {
transactionContainer.set(this, Integer.MAX_VALUE);
} catch (IllegalClassTypeException e) {
// TODO log
e.printStackTrace();
}
return _agents[_totalAgents-1];
}finally{
transactionContainer.disableAgentMode();
}
}
public JODBIndexingAgent getIndexingAgent(Field field, IOBase base){
int fieldId = base.getFieldSubstitutionID(field);
return getIndexingAgent(fieldId, base);
}
public JODBIndexingAgent getIndexingAgent(int fieldId, IOBase base){
for (int i = 0; i < _totalAgents; i++) {
if(_agents[i].getFieldId() == fieldId){
return _agents[i];
}
}
return null;
}
public synchronized void removeAgent(Field field, JODBOperationContext context){
int fieldId = context.getBase().getOrSetFieldSubstitutionID(field);
for (int i = 0; i < _totalAgents; i++) {
if(_agents[i].getFieldId() == fieldId){
TransactionContainer transactionContainer = context.getTransactionContainer();
try {
transactionContainer.delete(_agents[i], Integer.MAX_VALUE);
} catch (IllegalClassTypeException e) {
// TODO log
e.printStackTrace();
}
if(i < _totalAgents-1){
System.arraycopy(_agents, i+1, _agents, i, _totalAgents - i);
_agents[_totalAgents-1] = null;
}
_totalAgents--;
try {
transactionContainer.enableAgentMode();
transactionContainer.set(_agents, 1);
} catch (IllegalClassTypeException e) {
// TODO log
e.printStackTrace();
} finally{
transactionContainer.disableAgentMode();
}
break;
}
}
}
public static <T> T[] ensurePersistentArrayCapacity(T[] initialArray, int size, TransactionContainer container, int capacityIncrement){
if(size >= initialArray.length){
@SuppressWarnings("unchecked")
T[] newAgentsArray = (T[])java.lang.reflect.Array.newInstance(initialArray.getClass().getComponentType(), size+capacityIncrement);
System.arraycopy(initialArray, 0, newAgentsArray, 0, initialArray.length);
Arrays.fill(initialArray, null);
try {
container.delete(initialArray, 1);
} catch (IllegalClassTypeException e) {
//TODO log
e.printStackTrace();
}
initialArray = newAgentsArray;
}
return initialArray;
}
}