weka.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // Experimental
  2. var weka = weka || {};
  3. var json = json || require('./json');
  4. var java = {};
  5. weka.ModelFactory = class {
  6. match(context) {
  7. try {
  8. const stream = context.stream;
  9. if (stream.length >= 5) {
  10. const signature = [ 0xac, 0xed ];
  11. if (stream.peek(2).every((value, index) => value === signature[index])) {
  12. const reader = new java.io.InputObjectStream(stream);
  13. const obj = reader.read();
  14. if (obj && obj.$class && obj.$class.name) {
  15. return 'weka';
  16. }
  17. }
  18. }
  19. }
  20. catch (err) {
  21. // continue regardless of error
  22. }
  23. return undefined;
  24. }
  25. open(context) {
  26. return Promise.resolve().then(() => {
  27. const reader = new java.io.InputObjectStream(context.stream);
  28. const obj = reader.read();
  29. throw new weka.Error("Unsupported type '" + obj.$class.name + "'.");
  30. });
  31. }
  32. };
  33. weka.Error = class extends Error {
  34. constructor(message) {
  35. super(message);
  36. this.name = 'Error loading Weka model.';
  37. }
  38. };
  39. java.io = {};
  40. java.io.InputObjectStream = class {
  41. constructor(stream) {
  42. // Object Serialization Stream Protocol
  43. // https://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/serialization/spec/protocol.doc.html
  44. if (stream.length < 5) {
  45. throw new java.io.Error('Invalid stream size');
  46. }
  47. const signature = [ 0xac, 0xed ];
  48. if (!stream.peek(2).every((value, index) => value === signature[index])) {
  49. throw new java.io.Error('Invalid stream signature');
  50. }
  51. this._reader = new java.io.InputObjectStream.BinaryReader(stream.peek());
  52. this._references = [];
  53. this._reader.skip(2);
  54. const version = this._reader.uint16();
  55. if (version !== 0x0005) {
  56. throw new java.io.Error("Unsupported version '" + version + "'.");
  57. }
  58. }
  59. read() {
  60. return this._object();
  61. }
  62. _object() {
  63. const code = this._reader.byte();
  64. switch (code) {
  65. case 0x73: { // TC_OBJECT
  66. const obj = {};
  67. obj.$class = this._classDesc();
  68. this._newHandle(obj);
  69. this._classData(obj);
  70. return obj;
  71. }
  72. case 0x74: { // TC_STRING
  73. return this._newString(false);
  74. }
  75. default: {
  76. throw new java.io.Error("Unsupported code '" + code + "'.");
  77. }
  78. }
  79. }
  80. _classDesc() {
  81. const code = this._reader.byte();
  82. switch (code) {
  83. case 0x72: // TC_CLASSDESC
  84. this._reader.skip(-1);
  85. return this._newClassDesc();
  86. case 0x71: // TC_REFERENCE
  87. return this._references[this._reader.uint32() - 0x7e0000];
  88. case 0x70: // TC_NULL
  89. this._reader.byte();
  90. return null;
  91. default:
  92. throw new java.io.Error("Unsupported code '" + code + "'.");
  93. }
  94. }
  95. _newClassDesc() {
  96. const code = this._reader.byte();
  97. switch (code) {
  98. case 0x72: { // TC_CLASSDESC
  99. const classDesc = {};
  100. classDesc.name = this._reader.string(),
  101. classDesc.id = this._reader.uint64().toString();
  102. this._newHandle(classDesc);
  103. classDesc.flags = this._reader.byte();
  104. classDesc.fields = [];
  105. const count = this._reader.uint16();
  106. for (let i = 0; i < count; i++) {
  107. const field = {};
  108. field.type = String.fromCharCode(this._reader.byte());
  109. field.name = this._reader.string();
  110. if (field.type === '[' || field.type === 'L') {
  111. field.classname = this._object();
  112. }
  113. classDesc.fields.push(field);
  114. }
  115. if (this._reader.byte() !== 0x78) {
  116. throw new java.io.Error('Expected TC_ENDBLOCKDATA.');
  117. }
  118. classDesc.superClass = this._classDesc();
  119. return classDesc;
  120. }
  121. case 0x7D: // TC_PROXYCLASSDESC
  122. return null;
  123. default:
  124. throw new java.io.Error("Unsupported code '" + code + "'.");
  125. }
  126. }
  127. _classData(/* obj */) {
  128. /*
  129. const classname = obj.$class.name;
  130. let flags = obj.$class.flags;
  131. let superClass = obj.$class.superClass;
  132. while (superClass) {
  133. flags |= superClass.flags;
  134. superClass = superClass.superClass;
  135. }
  136. if (flags & 0x02) { // SC_SERIALIZABLE
  137. debugger;
  138. var customObject = objects[classname];
  139. var hasReadObjectMethod = customObject && customObject.readObject;
  140. if (flags & 0x01) { // SC_WRITE_METHOD
  141. if (!hasReadObjectMethod) {
  142. throw new Error('Class "'+ classname + '" dose not implement readObject()');
  143. }
  144. customObject.readObject(this, obj);
  145. if (this._reader.byte() !== 0x78) { // TC_ENDBLOCKDATA
  146. throw new java.io.Error('Expected TC_ENDBLOCKDATA.');
  147. }
  148. }
  149. else {
  150. if (hasReadObjectMethod) {
  151. customObject.readObject(this, obj);
  152. if (this._reader.byte() !== 0x78) { // TC_ENDBLOCKDATA
  153. throw new java.io.Error('Expected TC_ENDBLOCKDATA.');
  154. }
  155. }
  156. else {
  157. this._nowrclass(obj);
  158. }
  159. }
  160. }
  161. else if (flags & 0x04) { // SC_EXTERNALIZABLE
  162. if (flags & 0x08) { // SC_BLOCK_DATA
  163. this._objectAnnotation(obj);
  164. }
  165. else {
  166. this._externalContents();
  167. }
  168. }
  169. else {
  170. throw new Error('Illegal flags: ' + flags);
  171. }
  172. */
  173. }
  174. _newString(long) {
  175. const value = this._reader.string(long);
  176. this._newHandle(value);
  177. return value;
  178. }
  179. _newHandle(obj) {
  180. this._references.push(obj);
  181. }
  182. };
  183. java.io.InputObjectStream.BinaryReader = class {
  184. constructor(buffer) {
  185. this._buffer = buffer;
  186. this._position = 0;
  187. this._length = buffer.length;
  188. this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  189. }
  190. skip(offset) {
  191. this._position += offset;
  192. if (this._position > this._end) {
  193. throw new java.io.Error('Expected ' + (this._position - this._end) + ' more bytes. The file might be corrupted. Unexpected end of file.');
  194. }
  195. }
  196. byte() {
  197. const position = this._position;
  198. this.skip(1);
  199. return this._buffer[position];
  200. }
  201. uint16() {
  202. const position = this._position;
  203. this.skip(2);
  204. return this._view.getUint16(position, false);
  205. }
  206. uint32() {
  207. const position = this._position;
  208. this.skip(4);
  209. return this._view.getUint32(position, false);
  210. }
  211. uint64() {
  212. const position = this._position;
  213. this.skip(8);
  214. return this._view.getUint64(position, false);
  215. }
  216. string(long) {
  217. const size = long ? this.uint64().toNumber() : this.uint16();
  218. const position = this._position;
  219. this.skip(size);
  220. this._decoder = this._decoder || new TextDecoder('utf-8');
  221. return this._decoder.decode(this._buffer.subarray(position, this._position));
  222. }
  223. };
  224. java.io.Error = class extends Error {
  225. constructor(message) {
  226. super(message);
  227. this.name = 'Error loading Object Serialization Stream Protocol.';
  228. }
  229. };
  230. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  231. module.exports.ModelFactory = weka.ModelFactory;
  232. }