/*
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.transaction;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.BitSet;
import com.mobixess.jodb.core.IllegalClassTypeException;
import com.mobixess.jodb.core.JODBConfig;
import com.mobixess.jodb.core.JodbIOException;
import com.mobixess.jodb.core.JODBConstants.COMPARE_RESULT;
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.FieldRecord;
import com.mobixess.jodb.core.io.ObjectDataContainer.FieldsIterator;
import com.mobixess.jodb.core.plugin.IClassProcessor;
import com.mobixess.jodb.core.transaction.JODBSession.ClassDescriptor;
import com.mobixess.jodb.core.transaction.JODBSession.FieldAndIDRecord;
import com.mobixess.jodb.core.transaction.TransactionUtils.DataContainersCache;
import com.mobixess.jodb.util.ArrayUtils;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil;
import com.mobixess.jodb.util.PrimitiveJavaTypesUtil.PRIMITIVES_ENUMERATION;
public class BaseProcessor implements IClassProcessor {
public COMPARE_RESULT compare(Object value1, Object value2, JODBOperationContext context, Field[] fieldsToIgnore) {
if(equals(value1, value2, context, fieldsToIgnore)){
return COMPARE_RESULT.EQUAL;
}
return COMPARE_RESULT.UNKNOWN;
}
public COMPARE_RESULT compare(Object value1, ObjectDataContainer value2,
JODBOperationContext context, Field[] fieldsToIgnore, Class originalClass) throws IOException
{
if(equals(value1, value2, context, fieldsToIgnore)){
return COMPARE_RESULT.EQUAL;
}
return COMPARE_RESULT.UNKNOWN;
}
public Object composeInstance(Class type,
ObjectDataContainer persistentDataContainer, JODBSession session) throws IOException
{
ClassDescriptor classDescriptor;
classDescriptor = session.getDescriptorForClass(type);
if (persistentDataContainer.isArray()) {
return Array.newInstance(type, persistentDataContainer.getActiveFieldsIterator().getRemainingInCurrentCategory());
}
try {
return classDescriptor.newInstance();
} catch (IllegalClassTypeException e) {
throw new JodbIOException(e);
}
}
public boolean equals(Object value1, Object value2, JODBOperationContext context, Field[] fieldsToIgnore) {
if(value1 == value2){
return true;
}
if(value1 == null || value2 == null){
return false;
}
if(value1.getClass()!=value2.getClass()){
return false;
}
if(value1.getClass().isArray()){
return arraysAreEqual(value1, value2, context, fieldsToIgnore);
}else{
return objectsAreEqual(value1, value2, context, fieldsToIgnore);
}
}
private boolean objectsAreEqual(Object value1, Object value2, JODBOperationContext context, Field[] fieldsToIgnore) {
ClassDescriptor classDescriptor = context.getSession().getDescriptorForClass(value1.getClass());
FieldAndIDRecord[] fields = classDescriptor.getAllFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i]._field;
if(ArrayUtils.indexOf(fieldsToIgnore, field)!=-1 ){
continue;
}
try {
Object val1 = field.get(value1);
Object val2 = field.get(value2);
if(val1 == val2 || (field.getType().isPrimitive() && val1.equals(val2))){
continue;
}
if(val1!=null && (val1.getClass()==String.class) && val1.equals(val2)){
continue;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
return true;
}
public boolean arraysAreEqual(Object value1, Object value2, JODBOperationContext context, Field[] fieldsToIgnore) {
int size1 = Array.getLength(value1);
int size2 = Array.getLength(value2);
if(size1!=size2){
return false;
}
boolean isPrimitive = value1.getClass().getComponentType().isPrimitive();
for (int i = 0; i < size1; i++) {
Object val1 = Array.get(value1, i);
Object val2 = Array.get(value2, i);
if(isPrimitive){
if(!val1.equals(val2)){
return false;
}
}else{
if(val1!=val2){
return false;
}
}
}
return false;
}
public boolean equals(Object value1, ObjectDataContainer value2,
JODBOperationContext context, Field[] fieldsToIgnore) throws IOException
{
ClassDescriptor desc;
try {
desc = context.getSession().getDescriptorForClass(value1.getClass());
if (desc.isArray()) {
return checkActiveArrayObjectUnchanged(context, value1, value2, desc);
}
} catch (IllegalClassTypeException e) {
e.printStackTrace();
throw new JodbIOException(e);
}
return equalToPersistentCopy(context, value1, value2, desc, fieldsToIgnore);
}
public Object translate(Object objectToTranslate) {
return objectToTranslate;
}
public Class getType() {
return Object.class;
}
public void activate(Object incompleteInstance, ObjectDataContainer persistentDataContainer, JODBSession session, int activationDepth, boolean delayedActivation) throws IOException {
if(persistentDataContainer.isArray()){
fillArray(session, delayedActivation, persistentDataContainer, incompleteInstance, activationDepth);
}else{
fillObject(session, delayedActivation, persistentDataContainer, incompleteInstance, activationDepth);
}
}
private static void fillObject(JODBSession session, boolean delayedActivation, ObjectDataContainer objectDataContainer, Object instance, int remainingDepth) throws IOException{
ClassDescriptor classDescriptor = session.getDescriptorForClass(instance.getClass());
IOBase base = session.getBase();
FieldsIterator fieldsIterator = objectDataContainer.getActiveFieldsIterator();
if(fieldsIterator == null){
return;
}
BitSet processedFieldMask = new BitSet();
FieldRecord record = objectDataContainer.getRecordCache();
for (int i = 0; fieldsIterator.hasNext(); i++) {
fieldsIterator.next(record, base);
Field field = classDescriptor.getFieldForID(record._fieldID, processedFieldMask);
if (field == null) {
continue;
}
if (field.getType().isPrimitive() && !delayedActivation) {
try {
field.set(instance, record._value);
} catch (Exception e) {
setDefaultValue(instance, field);
//TODO add debug level output
}
continue;
}
if (remainingDepth < 1) {
continue;
}
Object child = TransactionUtils.launchObject(session, record._objectOffset, null, remainingDepth - 1);
try {
if(child!=null || JODBConfig.isAllowNullInReflectionSet()){
field.set(instance, child);
}
} catch (Exception e) {
setDefaultValue(instance, field);
//TODO add debug level output
continue;
}
}
if (!delayedActivation) {
FieldAndIDRecord[] fields = classDescriptor.getAllFields();
for (int i = 0; i < fields.length; i++) {//set default values for remaining fields
if (processedFieldMask.get(i)) {
continue;
}
setDefaultValue(instance, fields[i]._field);
}
}
}
private static void fillArray(JODBSession session,
boolean activation, ObjectDataContainer objectDataContainer,
Object instance, int remainingDepth) throws IOException
{
IOBase base = session.getBase();
FieldsIterator fieldsIterator = objectDataContainer.getActiveFieldsIterator();
if(fieldsIterator == null){
return;
}
FieldRecord record = objectDataContainer.getRecordCache();
ClassDescriptor arrayTypeDescriptor;
arrayTypeDescriptor = session.getDescriptorForClass(instance.getClass().getComponentType());
if (arrayTypeDescriptor.isPrimitive()) {
for (int i = 0; fieldsIterator.hasNext(); i++) {
fieldsIterator.next(record, base);
Array.set(instance, i, record._value);
}
} else {
for (int i = 0; fieldsIterator.hasNext(); i++) {
fieldsIterator.next(record, base);
Object child = TransactionUtils.launchObject(session, record._objectOffset, null, remainingDepth - 1);
Array.set(instance, i, child);
}
}
}
private static void setDefaultValue(Object obj,Field field){
try {
Class type = field.getType();
if(type.isPrimitive()){
Object value = PrimitiveJavaTypesUtil.getDefaultWrapperInstance(type.getName());
field.set(obj, value);
}else{
if(JODBConfig.isAllowNullInReflectionSet()){
field.set(obj, null);
}
}
} catch (Exception e) {
//TODO print for debug?
}
}
private static boolean equalToPersistentCopy(JODBOperationContext context, Object currentObject, ObjectDataContainer persistentCopyObjectDataContainer, ClassDescriptor desc , Field[] fieldsToIgnore) throws IOException{
//Field[] fields = desc.getFields();
JODBSession session = context.getSession();
//int[] fieldsIDs = desc.getFieldsSubstitutionIDs();
//boolean[] processedFields = new boolean[fieldsIDs.length];
FieldRecord fieldRecord = persistentCopyObjectDataContainer.getRecordCache();
FieldsIterator fieldsIterator = persistentCopyObjectDataContainer.getActiveFieldsIterator();
if(fieldsIterator == null){
return false;
}
while (fieldsIterator.hasNext()) {
fieldsIterator.next(fieldRecord, context.getBase(),false);
// int index = ArrayUtils.indexOf(fieldsIDs, fieldRecord._fieldID);
// if(index == -1){
// continue;//TODO the field is not exist anymore, should it indicate as "not equal" ?
// }
Field field = desc.getFieldForID(fieldRecord._fieldID, null);
if(field == null){
continue;//TODO the field is not exist anymore, should it indicate as "not equal" ?
}
// if( fieldsToIgnore!=null && ArrayUtils.indexOf(fieldsToIgnore, field)!=-1){
// processedFields[index] = true;
// continue;
// }
Object value;
try {
value = field.get(currentObject);
} catch (Exception e) {
throw new JodbIOException(e);
}
if(field.getType().isPrimitive()){
if( PrimitiveJavaTypesUtil.comparePrimitives(value, fieldRecord._primitiveRawDataBuffer) != 0 ){
return false;
}
}else{
if(value == null){//if object under the persistent offset is delete than we continue itteration
if(fieldRecord._objectOffset != 0){
DataContainersCache dataContainersCache = TransactionUtils.getObjectDataContainerCache();
ObjectDataContainer referencedObjectDataContainer = dataContainersCache.pullObjectDataContainer();
try {
referencedObjectDataContainer.readHeader(context.getIoTicket().getRandomAccessBuffer(), fieldRecord._objectOffset, true);
if(!referencedObjectDataContainer.isDeleted()){
return false;
}
} finally {
dataContainersCache.pushObjectDataContainer(referencedObjectDataContainer);
}
}
}else{
PersistentObjectHandle objectHandle = session.getHandleForActiveObject(value);
if(objectHandle == null || objectHandle.getObjectEntryOffset() != fieldRecord._objectOffset){
return false;
}
}
}
//processedFields[index] = true;
}
// TODO commented because iterrator now guarantee iterration through all field
// for (int i = 0; i < fieldsIDs.length; i++) {//cheking if remaining not found fields have default values
// if(fieldsIDs[i]==-1||processedFields[i]){
// continue;
// }
// Field field = fields[i];
// if(field.getType().isPrimitive()){
// if(!Utils.primitiveEquals(field, currentObject, 0)){
// return false;
// }
// }else{
// Object value;
// try {
// value = field.get(currentObject);
// } catch (Exception e) {
// e.printStackTrace();
// throw new IOException(e.getMessage());
// }
// if(value!=null){
// return false;
// }
// }
// }
return true;
}
private static boolean checkActiveArrayObjectUnchanged(JODBOperationContext context, Object array, ObjectDataContainer objectDataContainer, ClassDescriptor classDescriptor) throws IOException, IllegalClassTypeException{
//TODO make size estimation here?
int arraySize = Array.getLength(array);
FieldsIterator fieldsIterator = objectDataContainer.getActiveFieldsIterator();
if(fieldsIterator == null){
return false;//TODO add warning?
}
if(arraySize!=fieldsIterator.getRemainingInCurrentCategory()){
return true;
}
IOBase base = context.getBase();
JODBSession session = context.getSession();
FieldRecord fieldRecord = objectDataContainer.getRecordCache();
if( !classDescriptor.getArrayType().isPrimitive() ){
for (int i = 0; i < arraySize; i++) {
fieldsIterator.next(fieldRecord, base);
Object value = Array.get(array, i);
if(value == null){
if(fieldRecord._objectOffset!=0){
return false;
}
continue;
}
PersistentObjectHandle handle = session.getHandleForActiveObject(value);
if(handle == null || handle.getObjectEntryOffset() != fieldRecord._objectOffset){
return false;
}
}
}else{
PRIMITIVES_ENUMERATION enumeratedPrimitiveType = PrimitiveJavaTypesUtil.getEnumeratedType(classDescriptor.getArrayType().getName());
for (int i = 0; i < arraySize; i++) {
fieldsIterator.next(fieldRecord, base, false);
Object value = Array.get(array, i);
if( PrimitiveJavaTypesUtil.comparePrimitives(value, enumeratedPrimitiveType, fieldRecord._primitiveRawDataBuffer) != 0 ){
return false;
}
}
}
return true;
}
public int getUsageCondition() {
throw new RuntimeException("Not Implemented");
}
public boolean acceptType(Class typeOfObjectToProcess) {
return true;
}
}