private long createPropertyChain(Map<String, Object> properties) { if (properties == null || properties.isEmpty()) { return Record.NO_NEXT_PROPERTY.intValue(); } PropertyStore propStore = getPropertyStore(); List<PropertyRecord> propRecords = new ArrayList<PropertyRecord>(); PropertyRecord currentRecord = new PropertyRecord(propStore.nextId()); currentRecord.setInUse(true); currentRecord.setCreated(); propRecords.add(currentRecord); for (Entry<String, Object> entry : properties.entrySet()) { int keyId = indexHolder.getKeyId(entry.getKey()); if (keyId == -1) { keyId = createNewPropertyIndex(entry.getKey()); } PropertyBlock block = new PropertyBlock(); propStore.encodeValue(block, keyId, entry.getValue()); if (currentRecord.size() + block.getSize() > PropertyType.getPayloadSize()) { // Here it means the current block is done for PropertyRecord prevRecord = currentRecord; // Create new record long propertyId = propStore.nextId(); currentRecord = new PropertyRecord(propertyId); currentRecord.setInUse(true); currentRecord.setCreated(); // Set up links prevRecord.setNextProp(propertyId); currentRecord.setPrevProp(prevRecord.getId()); propRecords.add(currentRecord); // Now current is ready to start picking up blocks } currentRecord.addPropertyBlock(block); } /* * Add the property records in reverse order, which means largest * id first. That is to make sure we expand the property store file * only once. */ for (int i = propRecords.size() - 1; i >= 0; i--) { propStore.updateRecord(propRecords.get(i)); } /* * 0 will always exist, if the map was empty we wouldn't be here * and even one property will create at least one record. */ return propRecords.get(0).getId(); }
/** @return true if the passed primitive needs updating in the store. */ private boolean setPrimitiveProperty(PrimitiveRecord primitive, String name, Object value) { boolean result = false; long nextProp = primitive.getNextProp(); int index = indexHolder.getKeyId(name); if (index == -1) { index = createNewPropertyIndex(name); } PropertyBlock block = new PropertyBlock(); getPropertyStore().encodeValue(block, index, value); int size = block.getSize(); /* * current is the current record traversed * thatFits is the earliest record that can host the block * thatHas is the record that already has a block for this index */ PropertyRecord current = null, thatFits = null, thatHas = null; /* * We keep going while there are records or until we both found the * property if it exists and the place to put it, if exists. */ while (!(nextProp == Record.NO_NEXT_PROPERTY.intValue() || (thatHas != null && thatFits != null))) { current = getPropertyStore().getRecord(nextProp); /* * current.getPropertyBlock() is cheap but not free. If we already * have found thatHas, then we can skip this lookup. */ if (thatHas == null && current.getPropertyBlock(index) != null) { thatHas = current; PropertyBlock removed = thatHas.removePropertyBlock(index); if (removed.isLight()) { getPropertyStore().makeHeavy(removed); for (DynamicRecord dynRec : removed.getValueRecords()) { thatHas.addDeletedRecord(dynRec); } } getPropertyStore().updateRecord(thatHas); } /* * We check the size after we remove - potentially we can put in the same record. * * current.size() is cheap but not free. If we already found somewhere * where it fits, no need to look again. */ if (thatFits == null && (PropertyType.getPayloadSize() - current.size() >= size)) { thatFits = current; } nextProp = current.getNextProp(); } /* * thatHas is of no importance here. We know that the block is definitely not there. * However, we can be sure that if the property existed, thatHas is not null and does * not contain the block. * * thatFits is interesting. If null, we need to create a new record and link, otherwise * just add the block there. */ if (thatFits == null) { thatFits = new PropertyRecord(getPropertyStore().nextId()); thatFits.setInUse(true); result = true; if (primitive.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { PropertyRecord first = getPropertyStore().getRecord(primitive.getNextProp()); thatFits.setNextProp(first.getId()); first.setPrevProp(thatFits.getId()); getPropertyStore().updateRecord(first); } primitive.setNextProp(thatFits.getId()); } thatFits.addPropertyBlock(block); getPropertyStore().updateRecord(thatFits); return result; }