0001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
0002:
0003: This file is part of the db4o open source object database.
0004:
0005: db4o is free software; you can redistribute it and/or modify it under
0006: the terms of version 2 of the GNU General Public License as published
0007: by the Free Software Foundation and as clarified by db4objects' GPL
0008: interpretation policy, available at
0009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
0010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
0011: Suite 350, San Mateo, CA 94403, USA.
0012:
0013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
0014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
0015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0016: for more details.
0017:
0018: You should have received a copy of the GNU General Public License along
0019: with this program; if not, write to the Free Software Foundation, Inc.,
0020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
0021: package com.db4o.internal;
0022:
0023: import com.db4o.*;
0024: import com.db4o.config.*;
0025: import com.db4o.ext.*;
0026: import com.db4o.foundation.*;
0027: import com.db4o.internal.btree.*;
0028: import com.db4o.internal.handlers.*;
0029: import com.db4o.internal.marshall.*;
0030: import com.db4o.internal.query.processor.*;
0031: import com.db4o.internal.slots.*;
0032: import com.db4o.reflect.*;
0033: import com.db4o.reflect.generic.*;
0034:
0035: /**
0036: * @exclude
0037: */
0038: public class FieldMetadata implements StoredField {
0039:
0040: private ClassMetadata _containingClass;
0041:
0042: // position in ClassMetadata i_fields
0043: private int _arrayPosition;
0044:
0045: private String _name;
0046:
0047: private boolean _isArray;
0048:
0049: private boolean _isNArray;
0050:
0051: private boolean _isPrimitive;
0052:
0053: private ReflectField _javaField;
0054:
0055: TypeHandler4 _handler;
0056:
0057: protected int _handlerID;
0058:
0059: private int _state;
0060:
0061: private static final int NOT_LOADED = 0;
0062:
0063: private static final int UNAVAILABLE = -1;
0064:
0065: private static final int AVAILABLE = 1;
0066:
0067: private Config4Field _config;
0068:
0069: private Db4oTypeImpl _db4oType;
0070:
0071: private int _linkLength;
0072:
0073: private BTree _index;
0074:
0075: static final FieldMetadata[] EMPTY_ARRAY = new FieldMetadata[0];
0076:
0077: public FieldMetadata(ClassMetadata classMetadata) {
0078: _containingClass = classMetadata;
0079: }
0080:
0081: FieldMetadata(ClassMetadata containingClass,
0082: ObjectTranslator translator) {
0083: // for TranslatedFieldMetadata only
0084: this (containingClass);
0085: init(containingClass, translator.getClass().getName());
0086: _state = AVAILABLE;
0087: ObjectContainerBase stream = container();
0088: _handler = stream._handlers.handlerForClass(stream, stream
0089: .reflector()
0090: .forClass(translatorStoredClass(translator)));
0091: }
0092:
0093: protected final Class translatorStoredClass(
0094: ObjectTranslator translator) {
0095: try {
0096: return translator.storedClass();
0097: } catch (RuntimeException e) {
0098: throw new ReflectException(e);
0099: }
0100: }
0101:
0102: FieldMetadata(ClassMetadata containingClass,
0103: ObjectMarshaller marshaller) {
0104: // for CustomMarshallerFieldMetadata only
0105: this (containingClass);
0106: init(containingClass, marshaller.getClass().getName());
0107: _state = AVAILABLE;
0108: _handler = container()._handlers.untypedHandler();
0109: }
0110:
0111: FieldMetadata(ClassMetadata containingClass, ReflectField field,
0112: TypeHandler4 handler, int handlerID) {
0113: this (containingClass);
0114: init(containingClass, field.getName());
0115: _javaField = field;
0116: _javaField.setAccessible();
0117: _handler = handler;
0118: _handlerID = handlerID;
0119:
0120: // TODO: beautify !!! possibly pull up isPrimitive to ReflectField
0121: boolean isPrimitive = false;
0122: if (field instanceof GenericField) {
0123: isPrimitive = ((GenericField) field).isPrimitive();
0124: }
0125: configure(field.getFieldType(), isPrimitive);
0126: checkDb4oType();
0127: _state = AVAILABLE;
0128: }
0129:
0130: protected FieldMetadata(int handlerID, TypeHandler4 handler) {
0131: _handlerID = handlerID;
0132: _handler = handler;
0133: }
0134:
0135: /**
0136: * @param classMetadata
0137: * @param oldSlot
0138: */
0139: public void addFieldIndex(MarshallerFamily mf,
0140: ClassMetadata classMetadata, StatefulBuffer buffer,
0141: Slot oldSlot) throws FieldIndexException {
0142: if (!hasIndex()) {
0143: incrementOffset(buffer);
0144: return;
0145: }
0146:
0147: try {
0148: addIndexEntry(buffer, readIndexEntry(mf, buffer));
0149: } catch (CorruptionException exc) {
0150: throw new FieldIndexException(exc, this );
0151: }
0152: }
0153:
0154: protected void addIndexEntry(StatefulBuffer a_bytes,
0155: Object indexEntry) {
0156: addIndexEntry(a_bytes.getTransaction(), a_bytes.getID(),
0157: indexEntry);
0158: }
0159:
0160: public void addIndexEntry(Transaction trans, int parentID,
0161: Object indexEntry) {
0162: if (!hasIndex()) {
0163: return;
0164: }
0165:
0166: BTree index = getIndex(trans);
0167:
0168: // Although we checked hasIndex() already, we have to check
0169: // again here since index creation in YapFieldUUID can be
0170: // unsuccessful if it's called too early for PBootRecord.
0171: if (index == null) {
0172: return;
0173: }
0174: index.add(trans, createFieldIndexKey(parentID, indexEntry));
0175: }
0176:
0177: private FieldIndexKey createFieldIndexKey(int parentID,
0178: Object indexEntry) {
0179: Object convertedIndexEntry = indexEntryFor(indexEntry);
0180: return new FieldIndexKey(parentID, convertedIndexEntry);
0181: }
0182:
0183: protected Object indexEntryFor(Object indexEntry) {
0184: return _javaField.indexEntry(indexEntry);
0185: }
0186:
0187: public boolean canUseNullBitmap() {
0188: return true;
0189: }
0190:
0191: // alive() checked
0192: public Object readIndexEntry(MarshallerFamily mf,
0193: StatefulBuffer writer) throws CorruptionException,
0194: Db4oIOException {
0195: return ((IndexableTypeHandler) _handler).readIndexEntry(mf,
0196: writer);
0197: }
0198:
0199: public void removeIndexEntry(Transaction trans, int parentID,
0200: Object indexEntry) {
0201: if (!hasIndex()) {
0202: return;
0203: }
0204:
0205: if (_index == null) {
0206: return;
0207: }
0208: _index.remove(trans, createFieldIndexKey(parentID, indexEntry));
0209: }
0210:
0211: public boolean alive() {
0212: if (_state == AVAILABLE) {
0213: return true;
0214: }
0215: if (_state == NOT_LOADED) {
0216:
0217: if (_handler == null) {
0218:
0219: // this may happen if the local ClassMetadataRepository
0220: // has not been updated from the server and presumably
0221: // in some refactoring cases.
0222:
0223: // We try to heal the problem by re-reading the class.
0224:
0225: // This could be dangerous, if the class type of a field
0226: // has been modified.
0227:
0228: // TODO: add class refactoring features
0229:
0230: _handler = loadJavaField1();
0231: checkHandlerID();
0232: }
0233:
0234: loadJavaField();
0235:
0236: if (_handler != null) {
0237: // TODO: This part is not quite correct.
0238: // We are using the old array information read from file to wrap.
0239:
0240: // If a schema evolution changes an array to a different variable,
0241: // we are in trouble here.
0242: _handler = wrapHandlerToArrays(container(), _handler);
0243: }
0244:
0245: if (_handler == null || _javaField == null) {
0246: _state = UNAVAILABLE;
0247: _javaField = null;
0248: } else {
0249: _state = AVAILABLE;
0250: checkDb4oType();
0251: }
0252: }
0253: return _state == AVAILABLE;
0254: }
0255:
0256: private void checkHandlerID() {
0257: if (!(_handler instanceof ClassMetadata)) {
0258: return;
0259: }
0260: ClassMetadata classMetadata = (ClassMetadata) _handler;
0261: int id = classMetadata.getID();
0262:
0263: if (_handlerID == 0) {
0264: _handlerID = id;
0265: return;
0266: }
0267: if (id > 0 && id != _handlerID) {
0268: // wrong type, refactoring, field should be turned off
0269: _handler = null;
0270: }
0271: }
0272:
0273: boolean canAddToQuery(String fieldName) {
0274: if (!alive()) {
0275: return false;
0276: }
0277: return fieldName.equals(getName()) && containingClass() != null
0278: && !containingClass().isInternal();
0279: }
0280:
0281: public boolean canHold(ReflectClass claxx) {
0282: // alive() is checked in QField caller
0283: if (claxx == null) {
0284: return !_isPrimitive;
0285: }
0286: return Handlers4.handlerCanHold(_handler, claxx);
0287: }
0288:
0289: public Object coerce(ReflectClass claxx, Object obj) {
0290: // alive() is checked in QField caller
0291:
0292: if (claxx == null || obj == null) {
0293: return _isPrimitive ? No4.INSTANCE : obj;
0294: }
0295:
0296: if (_handler instanceof PrimitiveHandler) {
0297: return ((PrimitiveHandler) _handler).coerce(claxx, obj);
0298: }
0299:
0300: if (!canHold(claxx)) {
0301: return No4.INSTANCE;
0302: }
0303:
0304: return obj;
0305: }
0306:
0307: public final boolean canLoadByIndex() {
0308: if (_handler instanceof ClassMetadata) {
0309: ClassMetadata yc = (ClassMetadata) _handler;
0310: if (yc.isArray()) {
0311: return false;
0312: }
0313: }
0314: return true;
0315: }
0316:
0317: void cascadeActivation(Transaction trans, Object onObject,
0318: int depth, boolean activate) {
0319: if (!alive()) {
0320: return;
0321: }
0322: if (!(_handler instanceof FirstClassHandler)) {
0323: return;
0324: }
0325: FirstClassHandler firstClassHandler = (FirstClassHandler) _handler;
0326:
0327: try {
0328: Object cascadeTo = getOrCreate(trans, onObject);
0329: if (cascadeTo == null) {
0330: return;
0331: }
0332: firstClassHandler.cascadeActivation(trans, cascadeTo,
0333: depth, activate);
0334: } catch (Exception e) {
0335: // FIXME: Catch all
0336: }
0337: }
0338:
0339: private void checkDb4oType() {
0340: if (_javaField != null) {
0341: if (container()._handlers.ICLASS_DB4OTYPE
0342: .isAssignableFrom(_javaField.getFieldType())) {
0343: _db4oType = HandlerRegistry.getDb4oType(_javaField
0344: .getFieldType());
0345: }
0346: }
0347: }
0348:
0349: void collectConstraints(Transaction a_trans, QConObject a_parent,
0350: Object a_template, Visitor4 a_visitor) {
0351: Object obj = getOn(a_trans, a_template);
0352: if (obj != null) {
0353: Collection4 objs = Platform4.flattenCollection(a_trans
0354: .container(), obj);
0355: Iterator4 j = objs.iterator();
0356: while (j.moveNext()) {
0357: obj = j.current();
0358: if (obj != null) {
0359:
0360: if (_isPrimitive) {
0361: if (_handler instanceof PrimitiveHandler) {
0362: if (obj
0363: .equals(((PrimitiveHandler) _handler)
0364: .primitiveNull())) {
0365: return;
0366: }
0367: }
0368: }
0369:
0370: if (Deploy.csharp) {
0371: if (Platform4.ignoreAsConstraint(obj)) {
0372: return;
0373: }
0374: }
0375: if (!a_parent.hasObjectInParentPath(obj)) {
0376: a_visitor.visit(new QConObject(a_trans,
0377: a_parent, qField(a_trans), obj));
0378: }
0379: }
0380: }
0381: }
0382: }
0383:
0384: public final TreeInt collectIDs(MarshallerFamily mf, TreeInt tree,
0385: StatefulBuffer a_bytes) throws FieldIndexException {
0386: if (!alive()) {
0387: return tree;
0388: }
0389: if (_handler instanceof ClassMetadata) {
0390: return (TreeInt) Tree.add(tree, new TreeInt(a_bytes
0391: .readInt()));
0392: } else if (_handler instanceof ArrayHandler) {
0393: return ((ArrayHandler) _handler).collectIDs(mf, tree,
0394: a_bytes);
0395: }
0396: return tree;
0397: }
0398:
0399: void configure(ReflectClass clazz, boolean isPrimitive) {
0400: _isArray = clazz.isArray();
0401: if (_isArray) {
0402: ReflectArray reflectArray = container().reflector().array();
0403: _isNArray = reflectArray.isNDimensional(clazz);
0404: _isPrimitive = reflectArray.getComponentType(clazz)
0405: .isPrimitive();
0406: _handler = wrapHandlerToArrays(container(), _handler);
0407: } else {
0408: _isPrimitive = isPrimitive | clazz.isPrimitive();
0409: }
0410: }
0411:
0412: private final TypeHandler4 wrapHandlerToArrays(
0413: ObjectContainerBase container, TypeHandler4 handler) {
0414: if (_isNArray) {
0415: return new MultidimensionalArrayHandler(container, handler,
0416: arraysUsePrimitiveClassReflector());
0417: }
0418: if (_isArray) {
0419: return new ArrayHandler(container, handler,
0420: arraysUsePrimitiveClassReflector());
0421: }
0422: return handler;
0423: }
0424:
0425: private boolean arraysUsePrimitiveClassReflector() {
0426: return Deploy.csharp ? false : _isPrimitive;
0427: }
0428:
0429: void deactivate(Transaction a_trans, Object a_onObject, int a_depth) {
0430: if (!alive()) {
0431: return;
0432: }
0433: boolean isEnumClass = _containingClass.isEnum();
0434: if (_isPrimitive && !_isArray) {
0435: if (!isEnumClass) {
0436: _javaField.set(a_onObject,
0437: ((PrimitiveHandler) _handler).primitiveNull());
0438: }
0439: return;
0440: }
0441: if (a_depth > 0) {
0442: cascadeActivation(a_trans, a_onObject, a_depth, false);
0443: }
0444: if (!isEnumClass) {
0445: _javaField.set(a_onObject, null);
0446: }
0447: }
0448:
0449: /** @param isUpdate */
0450: public void delete(MarshallerFamily mf, StatefulBuffer a_bytes,
0451: boolean isUpdate) throws FieldIndexException {
0452: if (!checkAlive(a_bytes)) {
0453: return;
0454: }
0455:
0456: try {
0457: removeIndexEntry(mf, a_bytes);
0458: boolean dotnetValueType = false;
0459: if (Deploy.csharp) {
0460: dotnetValueType = Platform4
0461: .isValueType(getStoredType());
0462: }
0463: if ((_config != null && _config.cascadeOnDelete()
0464: .definiteYes())
0465: || dotnetValueType) {
0466: int preserveCascade = a_bytes.cascadeDeletes();
0467: a_bytes.setCascadeDeletes(1);
0468: _handler.deleteEmbedded(mf, a_bytes);
0469: a_bytes.setCascadeDeletes(preserveCascade);
0470: } else if (_config != null
0471: && _config.cascadeOnDelete().definiteNo()) {
0472: int preserveCascade = a_bytes.cascadeDeletes();
0473: a_bytes.setCascadeDeletes(0);
0474: _handler.deleteEmbedded(mf, a_bytes);
0475: a_bytes.setCascadeDeletes(preserveCascade);
0476: } else {
0477: _handler.deleteEmbedded(mf, a_bytes);
0478: }
0479: } catch (CorruptionException exc) {
0480: throw new FieldIndexException(exc, this );
0481: }
0482: }
0483:
0484: private final void removeIndexEntry(MarshallerFamily mf,
0485: StatefulBuffer a_bytes) throws CorruptionException,
0486: Db4oIOException {
0487: if (!hasIndex()) {
0488: return;
0489: }
0490: int offset = a_bytes._offset;
0491: Object obj = readIndexEntry(mf, a_bytes);
0492: removeIndexEntry(a_bytes.getTransaction(), a_bytes.getID(), obj);
0493: a_bytes._offset = offset;
0494: }
0495:
0496: public boolean equals(Object obj) {
0497: if (obj instanceof FieldMetadata) {
0498: FieldMetadata yapField = (FieldMetadata) obj;
0499: yapField.alive();
0500: alive();
0501: return yapField._isPrimitive == _isPrimitive
0502: && yapField._handler.equals(_handler)
0503: && yapField._name.equals(_name);
0504: }
0505: return false;
0506: }
0507:
0508: public int hashCode() {
0509: return _name.hashCode();
0510: }
0511:
0512: public final Object get(Object onObject) {
0513: return get(null, onObject);
0514: }
0515:
0516: public final Object get(Transaction trans, Object onObject) {
0517: if (_containingClass == null) {
0518: return null;
0519: }
0520: ObjectContainerBase container = container();
0521: if (container == null) {
0522: return null;
0523: }
0524: synchronized (container._lock) {
0525:
0526: // FIXME: The following is not really transactional.
0527: // This will work OK for normal C/S and for
0528: // single local mode but the transaction will
0529: // be wrong for MTOC.
0530: if (trans == null) {
0531: trans = container.transaction();
0532: }
0533:
0534: container.checkClosed();
0535: ObjectReference ref = trans.referenceForObject(onObject);
0536: if (ref == null) {
0537: return null;
0538: }
0539: int id = ref.getID();
0540: if (id <= 0) {
0541: return null;
0542: }
0543: UnmarshallingContext context = new UnmarshallingContext(
0544: trans, ref, Const4.ADD_TO_ID_TREE, false);
0545: return context.readFieldValue(this );
0546: }
0547: }
0548:
0549: public String getName() {
0550: return _name;
0551: }
0552:
0553: public final ClassMetadata handlerClassMetadata(
0554: ObjectContainerBase container) {
0555: // alive needs to be checked by all callers: Done
0556: TypeHandler4 handler = baseTypeHandler();
0557: if (Handlers4.handlesSimple(handler)) {
0558: return container._handlers.classMetadataForId(handlerID());
0559: }
0560: return (ClassMetadata) handler;
0561: }
0562:
0563: private TypeHandler4 baseTypeHandler() {
0564: return Handlers4.baseTypeHandler(_handler);
0565: }
0566:
0567: public TypeHandler4 getHandler() {
0568: // alive needs to be checked by all callers: Done
0569: return _handler;
0570: }
0571:
0572: public int handlerID() {
0573: // alive needs to be checked by all callers: Done
0574: return _handlerID;
0575: }
0576:
0577: /** @param trans */
0578: public Object getOn(Transaction trans, Object onObject) {
0579: if (alive()) {
0580: return _javaField.get(onObject);
0581: }
0582: return null;
0583: }
0584:
0585: /**
0586: * dirty hack for com.db4o.types some of them need to be set automatically
0587: * TODO: Derive from YapField for Db4oTypes
0588: */
0589: public Object getOrCreate(Transaction trans, Object onObject) {
0590: if (!alive()) {
0591: return null;
0592: }
0593: Object obj = _javaField.get(onObject);
0594: if (_db4oType != null && obj == null) {
0595: obj = _db4oType.createDefault(trans);
0596: _javaField.set(onObject, obj);
0597: }
0598: return obj;
0599: }
0600:
0601: public final ClassMetadata containingClass() {
0602: // alive needs to be checked by all callers: Done
0603: return _containingClass;
0604: }
0605:
0606: public ReflectClass getStoredType() {
0607: if (_javaField == null) {
0608: return null;
0609: }
0610: return Handlers4.baseType(_javaField.getFieldType());
0611: }
0612:
0613: public ObjectContainerBase container() {
0614: if (_containingClass == null) {
0615: return null;
0616: }
0617: return _containingClass.container();
0618: }
0619:
0620: public boolean hasConfig() {
0621: return _config != null;
0622: }
0623:
0624: public boolean hasIndex() {
0625: // alive needs to be checked by all callers: Done
0626: return _index != null;
0627: }
0628:
0629: public final void incrementOffset(Buffer buffer) {
0630: buffer.incrementOffset(linkLength());
0631: }
0632:
0633: public final void init(ClassMetadata containingClass, String name) {
0634: _containingClass = containingClass;
0635: _name = name;
0636: initIndex(containingClass, name);
0637: }
0638:
0639: final void initIndex(ClassMetadata containingClass, String name) {
0640: if (containingClass.config() == null) {
0641: return;
0642: }
0643: _config = containingClass.config().configField(name);
0644: if (Debug.configureAllFields && _config == null) {
0645: _config = (Config4Field) containingClass.config()
0646: .objectField(_name);
0647: }
0648: }
0649:
0650: public void init(int handlerID, boolean isPrimitive,
0651: boolean isArray, boolean isNArray) {
0652: _handlerID = handlerID;
0653: _isPrimitive = isPrimitive;
0654: _isArray = isArray;
0655: _isNArray = isNArray;
0656: }
0657:
0658: private boolean _initialized = false;
0659:
0660: final void initConfigOnUp(Transaction trans) {
0661: if (_config != null && !_initialized) {
0662: _initialized = true;
0663: _config.initOnUp(trans, this );
0664: }
0665: }
0666:
0667: public void instantiate(UnmarshallingContext context) {
0668: if (!checkAlive(context.buffer())) {
0669: return;
0670: }
0671: Object toSet = read(context);
0672: informAboutTransaction(toSet, context.transaction());
0673: set(context.persistentObject(), toSet);
0674: }
0675:
0676: private boolean checkAlive(Buffer buffer) {
0677: boolean alive = alive();
0678: if (!alive) {
0679: incrementOffset(buffer);
0680: }
0681: return alive;
0682: }
0683:
0684: private void informAboutTransaction(Object obj, Transaction trans) {
0685: if (_db4oType != null && obj != null) {
0686: ((Db4oTypeImpl) obj).setTrans(trans);
0687: }
0688: }
0689:
0690: public boolean isArray() {
0691: return _isArray;
0692: }
0693:
0694: protected int linkLength() {
0695: alive();
0696:
0697: if (_linkLength == 0) {
0698: _linkLength = calculateLinkLength();
0699: }
0700: return _linkLength;
0701: }
0702:
0703: private int calculateLinkLength() {
0704: if (_handler == null) {
0705: // must be ClassMetadata
0706: return Const4.ID_LENGTH;
0707: }
0708: if (_handler instanceof PersistentBase) {
0709: return ((PersistentBase) _handler).linkLength();
0710: }
0711: if (_handler instanceof PrimitiveHandler) {
0712: return ((PrimitiveHandler) _handler).linkLength();
0713: }
0714: if (_handler instanceof VariableLengthTypeHandler) {
0715: return ((VariableLengthTypeHandler) _handler).linkLength();
0716: }
0717:
0718: // TODO: For custom handlers there will have to be a way
0719: // to calculate the length in the slot.
0720:
0721: // Options:
0722:
0723: // (1) Remember when the first object is marshalled.
0724: // (2) Add a #defaultValue() method to TypeHandler4,
0725: // marshall the default value and check.
0726: // (3) Add a way to test the custom handler when it
0727: // is installed and remember the length there.
0728:
0729: throw new NotImplementedException();
0730: }
0731:
0732: public void loadHandler(ObjectContainerBase a_stream) {
0733: _handler = a_stream.handlerByID(_handlerID);
0734: }
0735:
0736: private void loadJavaField() {
0737: TypeHandler4 handler = loadJavaField1();
0738: if (handler == null || (!handler.equals(_handler))) {
0739: _javaField = null;
0740: _state = UNAVAILABLE;
0741: }
0742: }
0743:
0744: private TypeHandler4 loadJavaField1() {
0745: ReflectClass claxx = _containingClass.classReflector();
0746: if (claxx == null) {
0747: return null;
0748: }
0749: _javaField = claxx.getDeclaredField(_name);
0750: if (_javaField == null) {
0751: return null;
0752: }
0753: _javaField.setAccessible();
0754: ObjectContainerBase container = container();
0755: container.showInternalClasses(true);
0756: TypeHandler4 handlerForClass = container._handlers
0757: .handlerForClass(container, _javaField.getFieldType());
0758: container.showInternalClasses(false);
0759: return handlerForClass;
0760: }
0761:
0762: private int adjustUpdateDepth(Object obj, int updateDepth) {
0763: int minimumUpdateDepth = 1;
0764: if (_containingClass.isCollection(obj)) {
0765: GenericReflector reflector = _containingClass.reflector();
0766: minimumUpdateDepth = reflector
0767: .collectionUpdateDepth(reflector.forObject(obj));
0768: }
0769: if (updateDepth < minimumUpdateDepth) {
0770: return minimumUpdateDepth;
0771: }
0772: return updateDepth;
0773: }
0774:
0775: private boolean cascadeOnUpdate(
0776: Config4Class parentClassConfiguration) {
0777: return ((parentClassConfiguration != null && (parentClassConfiguration
0778: .cascadeOnUpdate().definiteYes())) || (_config != null && (_config
0779: .cascadeOnUpdate().definiteYes())));
0780: }
0781:
0782: public void marshall(MarshallingContext context, Object obj) {
0783: // alive needs to be checked by all callers: Done
0784: int updateDepth = context.updateDepth();
0785: if (obj != null
0786: && cascadeOnUpdate(context.classConfiguration())) {
0787: context.updateDepth(adjustUpdateDepth(obj, updateDepth));
0788: }
0789: context.createIndirection(_handler);
0790: _handler.write(context, obj);
0791: context.updateDepth(updateDepth);
0792: if (hasIndex()) {
0793: context.addIndexEntry(this , obj);
0794: }
0795: }
0796:
0797: public boolean needsArrayAndPrimitiveInfo() {
0798: return true;
0799: }
0800:
0801: public boolean needsHandlerId() {
0802: return true;
0803: }
0804:
0805: public Comparable4 prepareComparison(Object obj) {
0806: if (alive()) {
0807: _handler.prepareComparison(obj);
0808: return _handler;
0809: }
0810: return null;
0811: }
0812:
0813: public QField qField(Transaction a_trans) {
0814: int yapClassID = 0;
0815: if (_containingClass != null) {
0816: yapClassID = _containingClass.getID();
0817: }
0818: return new QField(a_trans, _name, this , yapClassID,
0819: _arrayPosition);
0820: }
0821:
0822: public Object read(InternalReadContext context) {
0823: if (!checkAlive(context.buffer())) {
0824: return null;
0825: }
0826: return context.read(_handler);
0827: }
0828:
0829: /**
0830: * @param trans
0831: * @param ref
0832: */
0833: public void readVirtualAttribute(Transaction trans, Buffer buffer,
0834: ObjectReference ref) {
0835: incrementOffset(buffer);
0836: }
0837:
0838: void refresh() {
0839: TypeHandler4 handler = loadJavaField1();
0840: if (handler != null) {
0841: handler = wrapHandlerToArrays(container(), handler);
0842: if (handler.equals(_handler)) {
0843: return;
0844: }
0845: }
0846: _javaField = null;
0847: _state = UNAVAILABLE;
0848: }
0849:
0850: // FIXME: needs test case
0851: public void rename(String newName) {
0852: ObjectContainerBase container = container();
0853: if (!container.isClient()) {
0854: _name = newName;
0855: _containingClass.setStateDirty();
0856: _containingClass.write(container.systemTransaction());
0857: } else {
0858: Exceptions4.throwRuntimeException(58);
0859: }
0860: }
0861:
0862: public void setArrayPosition(int a_index) {
0863: _arrayPosition = a_index;
0864: }
0865:
0866: public void set(Object onObject, Object obj) {
0867: // TODO: remove the following if and check callers
0868: if (null == _javaField)
0869: return;
0870: _javaField.set(onObject, obj);
0871: }
0872:
0873: void setName(String a_name) {
0874: _name = a_name;
0875: }
0876:
0877: boolean supportsIndex() {
0878: return alive() && (_handler instanceof Indexable4)
0879: && (!(_handler instanceof UntypedFieldHandler));
0880: }
0881:
0882: public final void traverseValues(final Visitor4 userVisitor) {
0883: if (!alive()) {
0884: return;
0885: }
0886: traverseValues(container().transaction(), userVisitor);
0887: }
0888:
0889: public final void traverseValues(final Transaction transaction,
0890: final Visitor4 userVisitor) {
0891: if (!alive()) {
0892: return;
0893: }
0894: assertHasIndex();
0895: ObjectContainerBase stream = transaction.container();
0896: if (stream.isClient()) {
0897: Exceptions4
0898: .throwRuntimeException(Messages.CLIENT_SERVER_UNSUPPORTED);
0899: }
0900: synchronized (stream.lock()) {
0901: _index.traverseKeys(transaction, new Visitor4() {
0902: public void visit(Object obj) {
0903: FieldIndexKey key = (FieldIndexKey) obj;
0904: userVisitor.visit(((IndexableTypeHandler) _handler)
0905: .indexEntryToObject(transaction, key
0906: .value()));
0907: }
0908: });
0909: }
0910: }
0911:
0912: private void assertHasIndex() {
0913: if (!hasIndex()) {
0914: Exceptions4
0915: .throwRuntimeException(Messages.ONLY_FOR_INDEXED_FIELDS);
0916: }
0917: }
0918:
0919: public String toString() {
0920: StringBuffer sb = new StringBuffer();
0921: if (_containingClass != null) {
0922: sb.append(_containingClass.getName());
0923: sb.append(".");
0924: sb.append(getName());
0925: }
0926: return sb.toString();
0927: }
0928:
0929: private void initIndex(Transaction systemTrans) {
0930: initIndex(systemTrans, 0);
0931: }
0932:
0933: public void initIndex(Transaction systemTrans, final int id) {
0934: if (_index != null) {
0935: throw new IllegalStateException();
0936: }
0937: if (systemTrans.container().isClient()) {
0938: return;
0939: }
0940: _index = newBTree(systemTrans, id);
0941: }
0942:
0943: protected final BTree newBTree(Transaction systemTrans, final int id) {
0944: ObjectContainerBase stream = systemTrans.container();
0945: Indexable4 indexHandler = indexHandler(stream);
0946: if (indexHandler == null) {
0947: if (Debug.atHome) {
0948: System.err.println("Could not create index for " + this
0949: + ": No index handler found");
0950: }
0951: return null;
0952: }
0953: return new BTree(systemTrans, id, new FieldIndexKeyHandler(
0954: stream, indexHandler));
0955: }
0956:
0957: protected Indexable4 indexHandler(ObjectContainerBase stream) {
0958: if (_javaField == null) {
0959: return null;
0960: }
0961: ReflectClass indexType = _javaField.indexType();
0962: TypeHandler4 classHandler = stream._handlers.handlerForClass(
0963: stream, indexType);
0964: if (!(classHandler instanceof Indexable4)) {
0965: return null;
0966: }
0967: return (Indexable4) classHandler;
0968: }
0969:
0970: /** @param trans */
0971: public BTree getIndex(Transaction trans) {
0972: return _index;
0973: }
0974:
0975: public boolean isVirtual() {
0976: return false;
0977: }
0978:
0979: public boolean isPrimitive() {
0980: return _isPrimitive;
0981: }
0982:
0983: public BTreeRange search(Transaction transaction, Object value) {
0984: assertHasIndex();
0985: Object transActionalValue = wrapWithTransactionContext(
0986: transaction, value);
0987: BTreeNodeSearchResult lowerBound = searchLowerBound(
0988: transaction, transActionalValue);
0989: BTreeNodeSearchResult upperBound = searchUpperBound(
0990: transaction, transActionalValue);
0991: return lowerBound.createIncludingRange(upperBound);
0992: }
0993:
0994: private Object wrapWithTransactionContext(Transaction transaction,
0995: Object value) {
0996: if (_handler instanceof ClassMetadata) {
0997: value = ((ClassMetadata) _handler)
0998: .wrapWithTransactionContext(transaction, value);
0999: }
1000: return value;
1001: }
1002:
1003: private BTreeNodeSearchResult searchUpperBound(
1004: Transaction transaction, final Object value) {
1005: return searchBound(transaction, Integer.MAX_VALUE, value);
1006: }
1007:
1008: private BTreeNodeSearchResult searchLowerBound(
1009: Transaction transaction, final Object value) {
1010: return searchBound(transaction, 0, value);
1011: }
1012:
1013: private BTreeNodeSearchResult searchBound(Transaction transaction,
1014: int parentID, Object keyPart) {
1015: return getIndex(transaction).searchLeaf(transaction,
1016: createFieldIndexKey(parentID, keyPart),
1017: SearchTarget.LOWEST);
1018: }
1019:
1020: public boolean rebuildIndexForClass(LocalObjectContainer stream,
1021: ClassMetadata yapClass) {
1022: // FIXME: BTree traversal over index here.
1023: long[] ids = yapClass.getIDs();
1024: for (int i = 0; i < ids.length; i++) {
1025: rebuildIndexForObject(stream, yapClass, (int) ids[i]);
1026: }
1027: return ids.length > 0;
1028: }
1029:
1030: /** @param classMetadata */
1031: protected void rebuildIndexForObject(LocalObjectContainer stream,
1032: final ClassMetadata classMetadata, final int objectId)
1033: throws FieldIndexException {
1034: StatefulBuffer writer = stream.readWriterByID(stream
1035: .systemTransaction(), objectId);
1036: if (writer != null) {
1037: rebuildIndexForWriter(stream, writer, objectId);
1038: } else {
1039: if (Deploy.debug) {
1040: throw new RuntimeException(
1041: "Unexpected null object for ID");
1042: }
1043: }
1044: }
1045:
1046: protected void rebuildIndexForWriter(LocalObjectContainer stream,
1047: StatefulBuffer writer, final int objectId) {
1048: ObjectHeader oh = new ObjectHeader(stream, writer);
1049: Object obj = readIndexEntryForRebuild(writer, oh);
1050: addIndexEntry(stream.systemTransaction(), objectId, obj);
1051: }
1052:
1053: private Object readIndexEntryForRebuild(StatefulBuffer writer,
1054: ObjectHeader oh) {
1055: return oh.objectMarshaller().readIndexEntry(oh.classMetadata(),
1056: oh._headerAttributes, this , writer);
1057: }
1058:
1059: public void dropIndex(Transaction systemTrans) {
1060: if (_index == null) {
1061: return;
1062: }
1063: ObjectContainerBase stream = systemTrans.container();
1064: if (stream.configImpl().messageLevel() > Const4.NONE) {
1065: stream.message("dropping index " + toString());
1066: }
1067: _index.free(systemTrans);
1068: stream.setDirtyInSystemTransaction(containingClass());
1069: _index = null;
1070: }
1071:
1072: public void defragField(MarshallerFamily mf, BufferPair readers) {
1073: getHandler().defrag(mf, readers, true);
1074: }
1075:
1076: public void createIndex() {
1077:
1078: if (hasIndex()) {
1079: return;
1080: }
1081: LocalObjectContainer container = (LocalObjectContainer) container();
1082:
1083: if (container.configImpl().messageLevel() > Const4.NONE) {
1084: container.message("creating index " + toString());
1085: }
1086: initIndex(container.systemTransaction());
1087: container.setDirtyInSystemTransaction(containingClass());
1088: reindex(container);
1089: }
1090:
1091: private void reindex(LocalObjectContainer container) {
1092: ClassMetadata clazz = containingClass();
1093: if (rebuildIndexForClass(container, clazz)) {
1094: container.systemTransaction().commit();
1095: }
1096: }
1097:
1098: }
|