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