package org.simantics.db.procore.cluster; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.procore.cluster.ClusterChangeSetI.Operation; import org.simantics.db.procore.cluster.ClusterChangeSetI.OperationEnum; import org.simantics.db.service.ClusterUID; class CCSParser { private static boolean DEBUG = false; enum StmEnum { Add, Remove }; enum ReferenceType { Local, ForeignShort, ForeignLong }; static class Args { ClusterUID clusterUID; ForeignTable rft; Operation rop; Args(ClusterUID clusterUID, ForeignTable rft, Operation rop) { this.clusterUID = clusterUID; this.rft = rft; this.rop = rop; } void createResource(short aRI) { rop.type = OperationEnum.CreateResource; rop.count = 1; rop.resourceIndex = aRI; } void addRelation(short aRI, short aPI, ClusterUID aPC, short aOI, ClusterUID aOC) { rop.type = OperationEnum.AddRelation; rop.count = 5; rop.resourceIndex = aRI; rop.predicateIndex = aPI; rop.predicateCluster = aPC; rop.objectIndex = aOI; rop.objectCluster = aOC; } void removeRelation(short aRI, short aPI, ClusterUID aPC, short aOI, ClusterUID aOC) { rop.type = OperationEnum.RemoveRelation; rop.count = 5; rop.resourceIndex = aRI; rop.predicateIndex = aPI; rop.predicateCluster = aPC; rop.objectIndex = aOI; rop.objectCluster = aOC; } void setValue(short aRI, byte[] apValueData, int valueStart, int aValueSize) { rop.type = OperationEnum.SetValue; rop.count = 4; rop.resourceIndex = aRI; rop.valueSize = aValueSize; rop.valueData = apValueData; rop.valueStart = valueStart; } void deleteValue(short aRI) { rop.type = OperationEnum.DeleteValue; rop.count = 1; rop.resourceIndex = aRI; } void modifyValue(short aRI, long aOffset, int aSize, byte[] apValue, int valueStart) { rop.type = OperationEnum.ModifyValue; rop.count = 5; rop.resourceIndex = aRI; rop.valueOffset = aOffset; rop.valueSize = aSize; rop.valueData = apValue; rop.valueStart = valueStart; } } private static class Data { short resource; ClusterUID clusterUID; boolean left; Data() { resource = 0; clusterUID = ClusterUID.Null; left = false; } } static class ForeignTable { ForeignTable() { init(); } void init() { mSize = 0; } boolean get(short key, Data data) { if (key < mSize) { data.resource = mTable[key].resource; data.clusterUID = mTable[key].clusterUID; data.left = mTable[key].left; return true; } return false; } void getOrThrow(short key, Data data) { if (key < mSize) { data.resource = mTable[key].resource; data.clusterUID = mTable[key].clusterUID; data.left = mTable[key].left; } else new IllegalArgumentException("Missing foreign table entry for key=" + key + "."); } // cid is just for debugging void put(ClusterUID clusterUID, final Data data) { if (mSize <256) { if (DEBUG) System.out.println("ft put c=" + clusterUID + " i=" + mSize + " r=" + data.resource + " rc=" + data.clusterUID); if (data.resource == 0) throw new RuntimeDatabaseException("Resource can not be zero."); if (data.clusterUID.equals(ClusterUID.Null)) throw new RuntimeDatabaseException("Cluster can not be zero."); mTable[mSize++] = data; } } private Data[] mTable = new Data[256]; private int mSize; } static int parseStm(StmEnum stmEnum, Args args, byte[] bytes, int ap, int aBits, // number of bits used from operation code ReferenceType aPredicate, ReferenceType aObject) { assert(aBits <= 6); byte left = (byte)(6 - aBits); short ri = (short)((bytes[ap] & (1< 0) { // middle index bits b = (short)(bytes[ap++] & 0xFF); l = 8; byte t = (byte)b; t &= (1<>= left; // skip used } GetLeftArgs a = new GetLeftArgs(ap, b, l); getLeft(args.clusterUID, args.rft, bytes, pdata, a); getLeft(args.clusterUID, args.rft, bytes, odata, a); ap = a.p; if (DEBUG) System.out.println("" + stmEnum + " r=" + ri + " rc=" + args.clusterUID + " p=" + pdata.resource + " pc=" + pdata.clusterUID + " o=" + odata.resource + " oc=" + odata.clusterUID); if (ClusterUID.Null.equals(args.clusterUID) || ClusterUID.Null.equals(pdata.clusterUID) || ClusterUID.Null.equals(odata.clusterUID)) throw new RuntimeDatabaseException("Illegal cluster uid. args=" + args); switch (stmEnum) { case Add: args.addRelation(ri, pdata.resource, pdata.clusterUID, odata.resource, odata.clusterUID); break; case Remove: args.removeRelation(ri, pdata.resource, pdata.clusterUID, odata.resource, odata.clusterUID); break; default: new RuntimeException("Internal error. Contact application support."); } return ap; } private static class GetLeftArgs { GetLeftArgs(int p, short b, byte l) { this.p = p; this.b = b; this.l = l; } int p; short b; byte l; } private static void getLeft(ClusterUID aClusterUID, ForeignTable rft, byte[] bytes, Data rdata, GetLeftArgs a) { if (rdata.left) { if (a.l < 6) { a.b |= ((bytes[a.p] & 0xFF) << a.l); // next 8 bits ++a.p; a.l += 8; } byte t = (byte)a.b; t &= 0x3F; rdata.resource |= t << 8; // top bits rdata.left = false; // just for debugging if (rdata.clusterUID != aClusterUID) rft.put(aClusterUID, rdata); a.b >>>= 6; // skip used bits a.l -= 6; } else // ForeignShort rft.getOrThrow(rdata.resource, rdata); } private static int getData(ClusterUID aClusterUID, ForeignTable ft, byte[] bytes, int rp, ReferenceType a, Data rdata) { switch (a) { default: throw new IllegalArgumentException("Illegal enumeration value=" + a + "."); case Local: rdata.resource = (short)(bytes[rp] & 0xFF); // low byte rdata.clusterUID = aClusterUID; rdata.left = true; rp += 1; break; case ForeignShort: rdata.resource = (short)(bytes[rp] & 0xFF); // index to foreign table rdata.left = false; rp += 1; break; case ForeignLong: rdata.clusterUID = ClusterUID.make(bytes, rp); rp += ClusterUID.getLongLength() * 8; rdata.resource = (short)(bytes[rp] & 0xFF); // low byte rdata.left = true; rp += 1; break; } return rp; } static long getLongN(byte[] bytes, int offset, int size) { long l = 0; for (int i=0, shift=0; i