/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o 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, see http://www.gnu.org/licenses/. */
package com.db4o.internal;
import com.db4o.*;
import com.db4o.activation.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.activation.*;
import com.db4o.internal.marshall.*;
import com.db4o.internal.slots.*;
import com.db4o.reflect.*;
import com.db4o.typehandlers.*;
/**
* A weak reference to an known object.
*
* "Known" ~ has been stored and/or retrieved within a transaction.
*
* References the corresponding ClassMetaData along with further metadata:
* internal id, UUID/version information, ...
*
* @exclude
*/
public class ObjectReference extends Identifiable implements ObjectInfo, Activator {
private ClassMetadata _class;
private Object _object;
private VirtualAttributes _virtualAttributes;
private ObjectReference _idPreceding;
private ObjectReference _idSubsequent;
private int _idSize;
private ObjectReference _hcPreceding;
private ObjectReference _hcSubsequent;
private int _hcSize;
public int _hcHashcode; // redundant hashCode
private int _lastTopLevelCallId;
public ObjectReference(){
}
public ObjectReference(int id) {
_id = id;
if(DTrace.enabled){
DTrace.OBJECT_REFERENCE_CREATED.log(id);
}
}
public ObjectReference(ClassMetadata classMetadata, int id) {
this(id);
_class = classMetadata;
}
public void activate(ActivationPurpose purpose) {
activateOn(container().transaction(), purpose);
}
public void activateOn(final Transaction transaction, ActivationPurpose purpose) {
if (activating()) {
return;
}
try {
activating(true);
final ObjectContainerBase container = transaction.container();
if (!(container.activationDepthProvider() instanceof TransparentActivationDepthProvider)) {
return;
}
final TransparentActivationDepthProvider provider = (TransparentActivationDepthProvider) container.activationDepthProvider();
if (ActivationPurpose.WRITE == purpose) {
synchronized(container.lock()){
provider.addModified(getObject(), transaction);
}
}
if (isActive()) {
return;
}
synchronized(container.lock()){
activate(transaction, getObject(), new DescendingActivationDepth(provider, ActivationMode.ACTIVATE));
}
}
finally {
activating(false);
}
}
private boolean activating() {
return bitIsTrue(Const4.ACTIVATING);
}
private void activating(boolean isActivating) {
if (isActivating) {
bitTrue(Const4.ACTIVATING);
} else {
bitFalse(Const4.ACTIVATING);
}
}
public void activate(Transaction ta, Object obj, ActivationDepth depth) {
final ObjectContainerBase container = ta.container();
activateInternal(container.activationContextFor(ta, obj, depth));
container.activatePending(ta);
}
void activateInternal(ActivationContext context) {
if (null == context) {
throw new ArgumentNullException();
}
if (!context.depth().requiresActivation()) {
return;
}
ObjectContainerBase container = context.container();
if (context.depth().mode().isRefresh()){
logActivation(container, "refresh");
} else {
if (isActive()) {
_class.cascadeActivation(context);
return;
}
logActivation(container, "activate");
}
readForActivation(context);
}
private void readForActivation(ActivationContext context) {
read(context.transaction(), null, context.targetObject(), context.depth(), Const4.ADD_MEMBERS_TO_ID_TREE_ONLY, false);
}
private void logActivation(ObjectContainerBase container, String event) {
logEvent(container, event, Const4.ACTIVATION);
}
private void logEvent(ObjectContainerBase container, String event, final int level) {
if (container.configImpl().messageLevel() > level) {
container.message("" + getID() + " " + event + " " + _class.getName());
}
}
/** return false if class not completely initialized, otherwise true **/
boolean continueSet(Transaction trans, UpdateDepth updateDepth) {
if (! bitIsTrue(Const4.CONTINUE)) {
return true;
}
if(! _class.stateOK()){
return false;
}
if(! _class.aspectsAreInitialized()){
return false;
}
if(DTrace.enabled){
DTrace.CONTINUESET.log(getID());
}
bitFalse(Const4.CONTINUE);
MarshallingContext context = new MarshallingContext(trans, this, updateDepth, true);
Handlers4.write(classMetadata().typeHandler(), context, getObject());
Pointer4 pointer = context.allocateSlot();
ByteArrayBuffer buffer = context.toWriteBuffer(pointer);
ObjectContainerBase container = trans.container();
container.writeNew(trans, pointer, _class, buffer);
Object obj = _object;
objectOnNew(trans, obj);
if(_class.hasIdentity()){
_object = container.newWeakReference(this, obj);
}
setStateClean();
endProcessing();
return true;
}
private void objectOnNew(Transaction transaction, Object obj) {
ObjectContainerBase container = transaction.container();
container.callbacks().objectOnNew(transaction, this);
_class.dispatchEvent(transaction, obj, EventDispatchers.NEW);
}
public void deactivate(Transaction trans, ActivationDepth depth) {
if (!depth.requiresActivation()) {
return;
}
Object obj = getObject();
if (obj == null) {
return;
}
ObjectContainerBase container = trans.container();
logActivation(container, "deactivate");
setStateDeactivated();
_class.deactivate(trans, this, depth);
}
public byte getIdentifier() {
return Const4.YAPOBJECT;
}
public long getInternalID() {
return getID();
}
public Object getObject() {
if (Platform4.hasWeakReferences()) {
return Platform4.getYapRefObject(_object);
}
return _object;
}
public Object getObjectReference(){
return _object;
}
public ObjectContainerBase container(){
if(_class == null){
throw new IllegalStateException();
}
return _class.container();
}
public Transaction transaction(){
return container().transaction();
}
public Db4oUUID getUUID(){
VirtualAttributes va = virtualAttributes(transaction());
if(va != null && va.i_database != null){
return new Db4oUUID(va.i_uuid, va.i_database.i_signature);
}
return null;
}
public long getVersion(){
return getCommitTimestamp();
}
public long getCommitTimestamp() {
synchronized (container().lock()) {
return container().systemTransaction().versionForId(getID());
}
}
public final ClassMetadata classMetadata() {
return _class;
}
public void classMetadata(ClassMetadata classMetadata) {
if (_class == classMetadata) {
return;
}
if (_class != null) {
throw new IllegalStateException("Object types aren't supposed to change!");
}
_class = classMetadata;
}
public int ownLength() {
throw Exceptions4.shouldNeverBeCalled();
}
public VirtualAttributes produceVirtualAttributes() {
if(_virtualAttributes == null){
_virtualAttributes = new VirtualAttributes();
}
return _virtualAttributes;
}
final void peekPersisted(Transaction trans, ActivationDepth depth) {
setObject(read(trans, depth, Const4.TRANSIENT, false));
}
final Object read(Transaction trans, ActivationDepth instantiationDepth,int addToIDTree,boolean checkIDTree) {
return read(trans, null, null, instantiationDepth, addToIDTree, checkIDTree);
}
public final Object read(
Transaction trans,
ByteArrayBuffer buffer,
Object obj,
ActivationDepth instantiationDepth,
int addToIDTree,
boolean checkIDTree) {
UnmarshallingContext context = new UnmarshallingContext(trans, buffer, this, addToIDTree, checkIDTree);
context.persistentObject(obj);
context.activationDepth(instantiationDepth);
return context.read();
}
public Object readPrefetch(Transaction trans, ByteArrayBuffer buffer, final int addToIDTree) {
final UnmarshallingContext context = new UnmarshallingContext(trans, buffer, this, addToIDTree, false);
context.activationDepth(new FixedActivationDepth(1, ActivationMode.PREFETCH));
return context.read();
}
public final void readThis(Transaction trans, ByteArrayBuffer buffer) {
if (Deploy.debug) {
System.out.println(
"YapObject.readThis should never be called. All handling takes place in read");
}
}
public void setObjectWeak(ObjectContainerBase container, Object obj) {
if(_object != null){
Platform4.killYapRef(_object);
}
_object = container.newWeakReference(this, obj);
}
public void setObject(Object obj) {
_object = obj;
}
final void store(Transaction trans, ClassMetadata classMetadata, Object obj){
_object = obj;
_class = classMetadata;
int id = trans.container().idForNewUserObject(trans);
setID(id);
// will be ended in continueset()
beginProcessing();
bitTrue(Const4.CONTINUE);
}
public void flagForDelete(int callId){
_lastTopLevelCallId = - callId;
}
public boolean isFlaggedForDelete(){
return _lastTopLevelCallId < 0;
}
public void flagAsHandled(int callId){
_lastTopLevelCallId = callId;
}
public final boolean isFlaggedAsHandled(int callID){
return _lastTopLevelCallId == callID;
}
public final boolean isValid() {
return isValidId(getID()) && getObject() != null;
}
public static final boolean isValidId(int id){
return id > 0;
}
public VirtualAttributes virtualAttributes(){
return _virtualAttributes;
}
public VirtualAttributes virtualAttributes(Transaction trans, boolean lastCommitted){
if(trans == null){
return _virtualAttributes;
}
synchronized(trans.container().lock()){
if(_virtualAttributes == null){
if(_class.hasVirtualAttributes()){
_virtualAttributes = new VirtualAttributes();
_class.readVirtualAttributes(trans, this, lastCommitted);
}
}else{
if(! _virtualAttributes.suppliesUUID()){
if(_class.hasVirtualAttributes()){
_class.readVirtualAttributes(trans, this, lastCommitted);
}
}
}
return _virtualAttributes;
}
}
public VirtualAttributes virtualAttributes(Transaction trans){
return virtualAttributes(trans, false);
}
public void setVirtualAttributes(VirtualAttributes at){
_virtualAttributes = at;
}
public void writeThis(Transaction trans, ByteArrayBuffer buffer) {
if (Deploy.debug) {
System.out.println("YapObject.writeThis should never be called.");
}
}
public void writeUpdate(Transaction transaction, UpdateDepth updatedepth) {
continueSet(transaction, updatedepth);
// make sure, a concurrent new, possibly triggered by objectOnNew
// is written to the file
// preventing recursive
if ( !beginProcessing() ) {
return;
}
Object obj = getObject();
if( !objectCanUpdate(transaction, obj) ||
!isActive() ||
obj == null ||
! classMetadata().isModified(obj)){
endProcessing();
return;
}
if (Deploy.debug) {
if (!(getID() > 0)) {
throw new IllegalStateException("ID invalid");
}
if (_class == null) {
throw new IllegalStateException("ClassMetadata invalid");
}
}
MarshallingContext context = new MarshallingContext(transaction, this, updatedepth, false);
if (context.updateDepth().negative()) {
endProcessing();
return;
}
ObjectContainerBase container = transaction.container();
logEvent(container, "update", Const4.STATE);
setStateClean();
context.purgeFieldIndexEntriesOnUpdate(transaction, container._handlers.arrayType(obj));
Handlers4.write(_class.typeHandler(), context, obj);
if(context.updateDepth().canSkip(this)) {
endProcessing();
return;
}
Pointer4 pointer = context.allocateSlot();
ByteArrayBuffer buffer = context.toWriteBuffer(pointer);
container.writeUpdate(transaction, pointer, _class, container._handlers.arrayType(obj), buffer);
if (isActive()) {
setStateClean();
}
endProcessing();
container.callbacks().objectOnUpdate(transaction, this);
classMetadata().dispatchEvent(transaction, obj, EventDispatchers.UPDATE);
}
protected boolean objectCanUpdate(Transaction transaction, Object obj) {
ObjectContainerBase container = transaction.container();
return container.callbacks().objectCanUpdate(transaction, this)
&& _class.dispatchEvent(transaction, obj, EventDispatchers.CAN_UPDATE);
}
public void ref_init() {
hc_init();
id_init();
}
/***** HCTREE *****/
public ObjectReference hc_add(ObjectReference newRef) {
if (newRef.getObject() == null) {
return this;
}
newRef.hc_init();
return hc_add1(newRef);
}
private void hc_init(){
_hcPreceding = null;
_hcSubsequent = null;
_hcSize = 1;
_hcHashcode = hc_getCode(getObject());
}
private ObjectReference hc_add1(ObjectReference newRef) {
int cmp = hc_compare(newRef);
if (cmp < 0) {
if (_hcPreceding == null) {
_hcPreceding = newRef;
_hcSize++;
} else {
_hcPreceding = _hcPreceding.hc_add1(newRef);
if (_hcSubsequent == null) {
return hc_rotateRight();
}
return hc_balance();
}
} else {
if (_hcSubsequent == null) {
_hcSubsequent = newRef;
_hcSize++;
} else {
_hcSubsequent = _hcSubsequent.hc_add1(newRef);
if (_hcPreceding == null) {
return hc_rotateLeft();
}
return hc_balance();
}
}
return this;
}
private ObjectReference hc_balance() {
int cmp = _hcSubsequent._hcSize - _hcPreceding._hcSize;
if (cmp < -2) {
return hc_rotateRight();
} else if (cmp > 2) {
return hc_rotateLeft();
} else {
_hcSize = _hcPreceding._hcSize + _hcSubsequent._hcSize + 1;
return this;
}
}
private void hc_calculateSize() {
if (_hcPreceding == null) {
if (_hcSubsequent == null) {
_hcSize = 1;
} else {
_hcSize = _hcSubsequent._hcSize + 1;
}
} else {
if (_hcSubsequent == null) {
_hcSize = _hcPreceding._hcSize + 1;
} else {
_hcSize = _hcPreceding._hcSize + _hcSubsequent._hcSize + 1;
}
}
}
private int hc_compare(ObjectReference toRef) {
int cmp = toRef._hcHashcode - _hcHashcode;
if(cmp == 0){
cmp = toRef._id - _id;
}
return cmp;
}
public ObjectReference hc_find(Object obj) {
return hc_find(hc_getCode(obj), obj);
}
private ObjectReference hc_find(int id, Object obj) {
int cmp = id - _hcHashcode;
if (cmp < 0) {
if (_hcPreceding != null) {
return _hcPreceding.hc_find(id, obj);
}
} else if (cmp > 0) {
if (_hcSubsequent != null) {
return _hcSubsequent.hc_find(id, obj);
}
} else {
if (obj == getObject()) {
return this;
}
if (_hcPreceding != null) {
ObjectReference inPreceding = _hcPreceding.hc_find(id, obj);
if (inPreceding != null) {
return inPreceding;
}
}
if (_hcSubsequent != null) {
return _hcSubsequent.hc_find(id, obj);
}
}
return null;
}
public static int hc_getCode(Object obj) {
int hcode = System.identityHashCode(obj);
if (hcode < 0) {
hcode = ~hcode;
}
return hcode;
}
private ObjectReference hc_rotateLeft() {
ObjectReference tree = _hcSubsequent;
_hcSubsequent = tree._hcPreceding;
hc_calculateSize();
tree._hcPreceding = this;
if(tree._hcSubsequent == null){
tree._hcSize = 1 + _hcSize;
}else{
tree._hcSize = 1 + _hcSize + tree._hcSubsequent._hcSize;
}
return tree;
}
private ObjectReference hc_rotateRight() {
ObjectReference tree = _hcPreceding;
_hcPreceding = tree._hcSubsequent;
hc_calculateSize();
tree._hcSubsequent = this;
if(tree._hcPreceding == null){
tree._hcSize = 1 + _hcSize;
}else{
tree._hcSize = 1 + _hcSize + tree._hcPreceding._hcSize;
}
return tree;
}
private ObjectReference hc_rotateSmallestUp() {
if (_hcPreceding != null) {
_hcPreceding = _hcPreceding.hc_rotateSmallestUp();
return hc_rotateRight();
}
return this;
}
public ObjectReference hc_remove(ObjectReference findRef) {
if (this == findRef) {
return hc_remove();
}
int cmp = hc_compare(findRef);
if (cmp <= 0) {
if (_hcPreceding != null) {
_hcPreceding = _hcPreceding.hc_remove(findRef);
}
}
if (cmp >= 0) {
if (_hcSubsequent != null) {
_hcSubsequent = _hcSubsequent.hc_remove(findRef);
}
}
hc_calculateSize();
return this;
}
public void hc_traverse(Visitor4 visitor){
if(_hcPreceding != null){
_hcPreceding.hc_traverse(visitor);
}
if(_hcSubsequent != null){
_hcSubsequent.hc_traverse(visitor);
}
// Traversing the leaves first allows to add ObjectReference
// nodes to different ReferenceSystem trees during commit
visitor.visit(this);
}
private ObjectReference hc_remove() {
if (_hcSubsequent != null && _hcPreceding != null) {
_hcSubsequent = _hcSubsequent.hc_rotateSmallestUp();
_hcSubsequent._hcPreceding = _hcPreceding;
_hcSubsequent.hc_calculateSize();
return _hcSubsequent;
}
if (_hcSubsequent != null) {
return _hcSubsequent;
}
return _hcPreceding;
}
/***** IDTREE *****/
public ObjectReference id_add(ObjectReference newRef) {
newRef.id_init();
return id_add1(newRef);
}
private void id_init() {
_idPreceding = null;
_idSubsequent = null;
_idSize = 1;
}
private ObjectReference id_add1(ObjectReference newRef) {
int cmp = newRef._id - _id;
if (cmp < 0) {
if (_idPreceding == null) {
_idPreceding = newRef;
_idSize++;
} else {
_idPreceding = _idPreceding.id_add1(newRef);
if (_idSubsequent == null) {
return id_rotateRight();
}
return id_balance();
}
} else if(cmp > 0) {
if (_idSubsequent == null) {
_idSubsequent = newRef;
_idSize++;
} else {
_idSubsequent = _idSubsequent.id_add1(newRef);
if (_idPreceding == null) {
return id_rotateLeft();
}
return id_balance();
}
}
return this;
}
private ObjectReference id_balance() {
int cmp = _idSubsequent._idSize - _idPreceding._idSize;
if (cmp < -2) {
return id_rotateRight();
} else if (cmp > 2) {
return id_rotateLeft();
} else {
_idSize = _idPreceding._idSize + _idSubsequent._idSize + 1;
return this;
}
}
private void id_calculateSize() {
if (_idPreceding == null) {
if (_idSubsequent == null) {
_idSize = 1;
} else {
_idSize = _idSubsequent._idSize + 1;
}
} else {
if (_idSubsequent == null) {
_idSize = _idPreceding._idSize + 1;
} else {
_idSize = _idPreceding._idSize + _idSubsequent._idSize + 1;
}
}
}
public ObjectReference id_find(int id) {
int cmp = id - _id;
if (cmp > 0) {
if (_idSubsequent != null) {
return _idSubsequent.id_find(id);
}
} else if (cmp < 0) {
if (_idPreceding != null) {
return _idPreceding.id_find(id);
}
} else {
return this;
}
return null;
}
private ObjectReference id_rotateLeft() {
ObjectReference tree = _idSubsequent;
_idSubsequent = tree._idPreceding;
id_calculateSize();
tree._idPreceding = this;
if(tree._idSubsequent == null){
tree._idSize = _idSize + 1;
}else{
tree._idSize = _idSize + 1 + tree._idSubsequent._idSize;
}
return tree;
}
private ObjectReference id_rotateRight() {
ObjectReference tree = _idPreceding;
_idPreceding = tree._idSubsequent;
id_calculateSize();
tree._idSubsequent = this;
if(tree._idPreceding == null){
tree._idSize = _idSize + 1;
}else{
tree._idSize = _idSize + 1 + tree._idPreceding._idSize;
}
return tree;
}
private ObjectReference id_rotateSmallestUp() {
if (_idPreceding != null) {
_idPreceding = _idPreceding.id_rotateSmallestUp();
return id_rotateRight();
}
return this;
}
public ObjectReference id_remove(ObjectReference ref) {
int cmp = ref._id - _id;
if (cmp < 0) {
if (_idPreceding != null) {
_idPreceding = _idPreceding.id_remove(ref);
}
} else if (cmp > 0) {
if (_idSubsequent != null) {
_idSubsequent = _idSubsequent.id_remove(ref);
}
} else {
if(this == ref){
return id_remove();
}
return this;
}
id_calculateSize();
return this;
}
private ObjectReference id_remove() {
if (_idSubsequent != null && _idPreceding != null) {
_idSubsequent = _idSubsequent.id_rotateSmallestUp();
_idSubsequent._idPreceding = _idPreceding;
_idSubsequent.id_calculateSize();
return _idSubsequent;
}
if (_idSubsequent != null) {
return _idSubsequent;
}
return _idPreceding;
}
public String toString(){
try{
int id = getID();
String str = "ObjectReference\nID=" + id;
Object obj = getObject();
if(obj == null && _class != null){
ObjectContainerBase container = _class.container();
if(container != null && id > 0){
obj = container.peekPersisted(container.transaction(), id, container.defaultActivationDepth(classMetadata()), true).toString();
}
}
if(obj == null){
str += "\nfor [null]";
}else{
String objToString ="";
try{
objToString = obj.toString();
}catch(Exception e){
}
if(classMetadata() != null){
ReflectClass claxx = classMetadata().reflector().forObject(obj);
str += "\n" + claxx.getName();
}
str += "\n" + objToString;
}
return str;
}catch(Exception e){
}
return "ObjectReference " + getID();
}
}