/* * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Simon McDuff - initial API and implementation * Ibrahim Sallam - code refactoring for CDO 3.0 */ package org.eclipse.emf.cdo.server.internal.objectivity.schema; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.server.internal.objectivity.bundle.OM; import org.eclipse.emf.cdo.server.internal.objectivity.db.ObjyClass; import org.eclipse.emf.cdo.server.internal.objectivity.db.ObjySchema; import org.eclipse.emf.cdo.server.internal.objectivity.mapper.ITypeMapper; import org.eclipse.net4j.util.om.trace.ContextTracer; import com.objy.as.app.Class_Object; import com.objy.as.app.Numeric_Value; import com.objy.as.app.Proposed_Class; import com.objy.as.app.String_Value; import com.objy.as.app.VArray_Object; import com.objy.as.app.d_Access_Kind; import com.objy.as.app.d_Module; import com.objy.as.app.ooBaseType; import com.objy.db.app.ooId; import com.objy.db.app.ooObj; /** * @author Simon McDuff */ /*** * This class we use VArray of fixed array of Strings, otherwise we have to create strigns as objects, which is * expensive. */ public class ObjyArrayListString { private static final ContextTracer TRACER_DEBUG = new ContextTracer(OM.DEBUG, ObjyArrayListString.class); public static String ClassName = "ObjyArrayListString"; // embedded class parts. private static String embeddedClassName = "oo_StringElement"; private static String embeddedAttributeName = "value"; private static String embeddedAttributeNull = "isNull"; // fixed array class parts. private static String FixedArrayClassName = "ObjyFixedStringArray"; private static String FixedElementsName = "elements"; private static final long FixedElementsSize = 10; protected Class_Object classObject; private VArray_Object vArray; transient long cacheSize = -1; transient long position; public static void buildSchema() { d_Module top_mod = ObjySchema.getTopModule(); if (top_mod.resolve_class(ObjyArrayListString.ClassName) == null) { if (TRACER_DEBUG.isEnabled()) { TRACER_DEBUG.trace("Schema not found for ooArrayListString. Adding ooArrayListString"); } boolean inProcess = top_mod.proposed_classes().hasNext(); // using embedded class (string, isNull) // Proposed_Class embeddedClass = new Proposed_Class(embeddedClassName); Proposed_Class embeddedClass = top_mod.propose_new_class(embeddedClassName); embeddedClass.add_embedded_class_attribute(com.objy.as.app.d_Module.LAST, d_Access_Kind.d_PUBLIC, // Access kind embeddedAttributeName, // Attribute name 1, // # elements in fixed-size array "ooUtf8String" // Type of numeric data ); // Default value embeddedClass.add_basic_attribute(com.objy.as.app.d_Module.LAST, d_Access_Kind.d_PUBLIC, // Access kind embeddedAttributeNull, // Attribute name 1, // # elements in fixed-size array ooBaseType.ooBOOLEAN // Type of numeric data ); // Default value // top_mod.propose_new_class(embeddedClass); // first the child class. // Proposed_Class fixedArrayClass = new Proposed_Class(ooArrayListString.FixedArrayClassName); Proposed_Class fixedArrayClass = top_mod.propose_new_class(ObjyArrayListString.FixedArrayClassName); fixedArrayClass.add_base_class(com.objy.as.app.d_Module.LAST, com.objy.as.app.d_Access_Kind.d_PUBLIC, "ooObj"); fixedArrayClass.add_embedded_class_attribute(com.objy.as.app.d_Module.LAST, d_Access_Kind.d_PUBLIC, // Access kind ObjyArrayListString.FixedElementsName, // Attribute name ObjyArrayListString.FixedElementsSize, // # elements in fixed-size array embeddedClassName // Type of numeric data ); // Default value // top_mod.propose_new_class(fixedArrayClass); // Proposed_Class StringArrayClass = new Proposed_Class(ooArrayListString.ClassName); Proposed_Class StringArrayClass = top_mod.propose_new_class(ObjyArrayListString.ClassName); StringArrayClass.add_base_class(com.objy.as.app.d_Module.LAST, com.objy.as.app.d_Access_Kind.d_PUBLIC, "ooObj"); StringArrayClass.add_basic_attribute(com.objy.as.app.d_Module.LAST, d_Access_Kind.d_PUBLIC, // Access kind ObjyArrayList.sizeName, // Attribute name 1, // # elements in fixed-size array ooBaseType.ooINT32 // Type of numeric data ); // Default value StringArrayClass.add_varray_attribute(com.objy.as.app.d_Module.LAST, d_Access_Kind.d_PUBLIC, // Access kind ObjyArrayList.arrayName, // Attribute name 1, false, ObjyArrayListString.FixedArrayClassName); // top_mod.propose_new_class(StringArrayClass); if (!inProcess) { top_mod.activate_proposals(true, true); } if (TRACER_DEBUG.isEnabled()) { TRACER_DEBUG.trace("SCHEMA changed : ooArrayListString added"); } } } public static void initObject(Class_Object classObject) { // set the size to 0; classObject.nset_numeric(ObjyArrayList.sizeName, new Numeric_Value(0)); } public ObjyArrayListString(Class_Object classObject) { this.classObject = classObject; } // TODO - there is no check if the index is out of bound. protected void setValue(long index, String newValue) { // find the chunk. long chunkItem = index / FixedElementsSize; long chunkIndex = index % FixedElementsSize; Class_Object chunkObject = getVArray().get_class_obj(chunkItem); // String_Value value = chunkObject.nget_string(FixedElementsName, chunkIndex); Class_Object embeddedElement = chunkObject.nget_class_obj(FixedElementsName, chunkIndex); // TODO - I don't like using magic numbers!!! we are using "1" to index into the embedded object. embeddedElement.set_numeric(1, newValue == null ? ITypeMapper.numericTrue : ITypeMapper.numericFalse); // String_Value value = embeddedElement.get_string(0); // TODO - I don't like using magic numbers!!! String_Value value = embeddedElement.nget_string(embeddedAttributeName); value.update(); if (newValue == null) { newValue = ""; } value.set(newValue); } protected String getValue(long index) { // find the chunk. long chunkItem = index / FixedElementsSize; long chunkIndex = index % FixedElementsSize; Class_Object chunkObject = getVArray().get_class_obj(chunkItem); // String_Value value = chunkObject.nget_string(FixedElementsName, chunkIndex); Class_Object embeddedElement = chunkObject.nget_class_obj(FixedElementsName, chunkIndex); // TODO - I don't like // using magic numbers!!! String_Value value = embeddedElement.nget_string(embeddedAttributeName); Numeric_Value isNull = embeddedElement.get_numeric(1); return isNull == ITypeMapper.numericTrue ? null : value.toString(); } public String[] getAll(int index, int chunkSize) { int size = (int)cachedSize(); if (chunkSize != CDORevision.UNCHUNKED) { size = Math.min(size, chunkSize); } String[] strings = new String[size]; Class_Object currentChunkObject = null; long currentChunkItem = -1; for (int i = 0; i < size; i++) { // find the chunk. long chunkItem = (i + index) / FixedElementsSize; long chunkIndex = (i + index) % FixedElementsSize; if (currentChunkItem != chunkItem) { currentChunkItem = chunkItem; currentChunkObject = getVArray().get_class_obj(currentChunkItem); } Class_Object embeddedElement = currentChunkObject.nget_class_obj(FixedElementsName, chunkIndex); String_Value value = embeddedElement.nget_string(embeddedAttributeName); Numeric_Value isNull = embeddedElement.get_numeric(1); if (isNull == ITypeMapper.numericTrue) { strings[i] = null; } else { strings[i] = value.toString(); } } return strings; } public void clear() { // remove all the fixed array objects... for (int i = 0; i < getVArray().size(); i++) { ooId oid = getVArray().get_ooId(i); ooObj.create_ooObj(oid).delete(); } getVArray().resize(0); cacheSize = 0; saveSize(); } private void shiftRight(int index) { shiftRight(index, 1); } private void shiftRight(int index, int sizeToShift) { long size = cachedSize(); for (long i = size - 1; i >= index; i--) { setValue(i + sizeToShift, getValue(i)); } cacheSize += sizeToShift; } private void shiftLeft(int index) { long size = cachedSize(); for (long i = index; i < size - 1; i++) { setValue(i, getValue(i + 1)); } cacheSize--; saveSize(); } /** * */ private void grow(int numToAdd) { long arraySize = cachedSize(); long numChunks = (numToAdd + (int)arraySize) / FixedElementsSize + 1; long newChunks = numChunks - arraySize; if (newChunks > 0) { getVArray().resize(numChunks); // TODO - this could be cached somewhere... ObjyClass chunkClass = ObjySchema.getObjyClass(FixedArrayClassName); for (int i = 0; i < newChunks; i++) { // create a new chunk. Class_Object newChunk = Class_Object.new_persistent_object(chunkClass.getASClass(), classObject.objectID(), false); getVArray().set_ooId(arraySize + i, newChunk.objectID()); } cacheSize = -1; } } /** * */ private void prepareToInsert(int numberToAdd) { long size = cachedSize(); getVArray().update(); if (size + numberToAdd > getVArray().size()) { grow(numberToAdd); } } protected VArray_Object getVArray() { if (vArray == null) { vArray = classObject.nget_varray(ObjyArrayList.arrayName); } return vArray; } public void add(int index, String newValue) { prepareToInsert(1); shiftRight(index); basicSet(index, newValue); saveSize(); } public void addAll(int index, Object[] newValue) { prepareToInsert(newValue.length); shiftRight(index, newValue.length); for (int i = 0; i < newValue.length; i++) { basicSet(index + i, (String)newValue[i]); } saveSize(); } public void remove(int index) { shiftLeft(index); } public void add(String newValue) { long size = cachedSize(); prepareToInsert(1); setValue(size, newValue); cacheSize++; saveSize(); } public void set(long index, String newValue) { basicSet(index, newValue); cacheSize = -1; } protected void basicSet(long index, String newValue) { if (index >= cachedSize()) { throw new ArrayIndexOutOfBoundsException(); } getVArray().update(); setValue(index, newValue); } public String get(long index) { if (index >= size()) { throw new ArrayIndexOutOfBoundsException(); } return getValue(index); } private void saveSize() { classObject.nset_numeric(ObjyArrayList.sizeName, new Numeric_Value(cacheSize)); cacheSize = -1; } public long cachedSize() { if (cacheSize == -1) { cacheSize = classObject.nget_numeric(ObjyArrayList.sizeName).longValue(); } return cacheSize; } public long size() { return classObject.nget_numeric(ObjyArrayList.sizeName).longValue(); } }