/src/sys/java/fan/sys/OutStream.java

https://bitbucket.org/bedlaczech/fan-1.0 · Java · 477 lines · 391 code · 45 blank · 41 comment · 48 complexity · 8d0e0edec7a2750f989a5277b130dcb8 MD5 · raw file

  1. //
  2. // Copyright (c) 2006, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 27 Mar 06 Brian Frank Creation
  7. //
  8. package fan.sys;
  9. import java.math.*;
  10. import fanx.serial.*;
  11. /**
  12. * OutStream.
  13. */
  14. public class OutStream
  15. extends FanObj
  16. {
  17. //////////////////////////////////////////////////////////////////////////
  18. // Construction
  19. //////////////////////////////////////////////////////////////////////////
  20. public static OutStream make(OutStream out)
  21. {
  22. OutStream self = new OutStream();
  23. make$(self, out);
  24. return self;
  25. }
  26. public static void make$(OutStream self, OutStream out)
  27. {
  28. self.out = out;
  29. if (out != null) self.charset(out.charset());
  30. }
  31. //////////////////////////////////////////////////////////////////////////
  32. // Obj
  33. //////////////////////////////////////////////////////////////////////////
  34. public Type typeof() { return Sys.OutStreamType; }
  35. //////////////////////////////////////////////////////////////////////////
  36. // Java OutputStream
  37. //////////////////////////////////////////////////////////////////////////
  38. /**
  39. * Write a byte using a Java primitive int. Most
  40. * writes route to this method for efficient mapping to
  41. * a java.io.OutputStream. If we aren't overriding this
  42. * method, then route back to write(Int) for the
  43. * subclass to handle.
  44. */
  45. public OutStream w(int b)
  46. {
  47. return write(b);
  48. }
  49. //////////////////////////////////////////////////////////////////////////
  50. // OutStream
  51. //////////////////////////////////////////////////////////////////////////
  52. public OutStream write(long x)
  53. {
  54. try
  55. {
  56. out.write(x);
  57. return this;
  58. }
  59. catch (NullPointerException e)
  60. {
  61. if (out == null)
  62. throw UnsupportedErr.make(typeof().qname() + " wraps null OutStream");
  63. else
  64. throw e;
  65. }
  66. }
  67. public OutStream writeBuf(Buf buf) { return writeBuf(buf, buf.remaining()); }
  68. public OutStream writeBuf(Buf buf, long n)
  69. {
  70. try
  71. {
  72. out.writeBuf(buf, n);
  73. return this;
  74. }
  75. catch (NullPointerException e)
  76. {
  77. if (out == null)
  78. throw UnsupportedErr.make(typeof().qname() + " wraps null OutStream");
  79. else
  80. throw e;
  81. }
  82. }
  83. public Endian endian()
  84. {
  85. return bigEndian ? Endian.big : Endian.little;
  86. }
  87. public void endian(Endian endian)
  88. {
  89. bigEndian = endian == Endian.big;
  90. }
  91. public OutStream writeI2(long x)
  92. {
  93. int v = (int)x;
  94. if (bigEndian)
  95. return this.w((v >>> 8) & 0xFF)
  96. .w((v >>> 0) & 0xFF);
  97. else
  98. return this.w((v >>> 0) & 0xFF)
  99. .w((v >>> 8) & 0xFF);
  100. }
  101. public OutStream writeI4(long x)
  102. {
  103. int v = (int)x;
  104. if (bigEndian)
  105. return this.w((v >>> 24) & 0xFF)
  106. .w((v >>> 16) & 0xFF)
  107. .w((v >>> 8) & 0xFF)
  108. .w((v >>> 0) & 0xFF);
  109. else
  110. return this.w((v >>> 0) & 0xFF)
  111. .w((v >>> 8) & 0xFF)
  112. .w((v >>> 16) & 0xFF)
  113. .w((v >>> 24) & 0xFF);
  114. }
  115. public OutStream writeI8(long v)
  116. {
  117. if (bigEndian)
  118. return this.w((int)(v >>> 56) & 0xFF)
  119. .w((int)(v >>> 48) & 0xFF)
  120. .w((int)(v >>> 40) & 0xFF)
  121. .w((int)(v >>> 32) & 0xFF)
  122. .w((int)(v >>> 24) & 0xFF)
  123. .w((int)(v >>> 16) & 0xFF)
  124. .w((int)(v >>> 8) & 0xFF)
  125. .w((int)(v >>> 0) & 0xFF);
  126. else
  127. return this.w((int)(v >>> 0) & 0xFF)
  128. .w((int)(v >>> 8) & 0xFF)
  129. .w((int)(v >>> 16) & 0xFF)
  130. .w((int)(v >>> 24) & 0xFF)
  131. .w((int)(v >>> 32) & 0xFF)
  132. .w((int)(v >>> 40) & 0xFF)
  133. .w((int)(v >>> 48) & 0xFF)
  134. .w((int)(v >>> 56) & 0xFF);
  135. }
  136. public OutStream writeF4(double x)
  137. {
  138. return writeI4(Float.floatToIntBits((float)x));
  139. }
  140. public OutStream writeF8(double x)
  141. {
  142. return writeI8(Double.doubleToLongBits(x));
  143. }
  144. public OutStream writeDecimal(BigDecimal x)
  145. {
  146. return writeUtf(x.toString());
  147. }
  148. public OutStream writeBool(boolean x)
  149. {
  150. return w(x ? 1 : 0);
  151. }
  152. public OutStream writeUtf(String s)
  153. {
  154. int slen = s.length();
  155. int utflen = 0;
  156. // first we have to figure out the utf length
  157. for (int i=0; i<slen; ++i)
  158. {
  159. int c = s.charAt(i);
  160. if (c <= 0x007F)
  161. utflen +=1;
  162. else if (c > 0x07FF)
  163. utflen += 3;
  164. else
  165. utflen += 2;
  166. }
  167. // sanity check
  168. if (utflen > 65536) throw IOErr.make("String too big");
  169. // write length as 2 byte value
  170. w((utflen >>> 8) & 0xFF);
  171. w((utflen >>> 0) & 0xFF);
  172. // write characters
  173. for (int i=0; i<slen; ++i)
  174. {
  175. int c = s.charAt(i);
  176. if (c <= 0x007F)
  177. {
  178. w(c);
  179. }
  180. else if (c > 0x07FF)
  181. {
  182. w(0xE0 | ((c >> 12) & 0x0F));
  183. w(0x80 | ((c >> 6) & 0x3F));
  184. w(0x80 | ((c >> 0) & 0x3F));
  185. }
  186. else
  187. {
  188. w(0xC0 | ((c >> 6) & 0x1F));
  189. w(0x80 | ((c >> 0) & 0x3F));
  190. }
  191. }
  192. return this;
  193. }
  194. public Charset charset()
  195. {
  196. return charset;
  197. }
  198. public void charset(Charset charset)
  199. {
  200. this.charsetEncoder = charset.newEncoder();
  201. this.charset = charset;
  202. }
  203. public OutStream writeChar(long c) { return writeChar((char)c); }
  204. public OutStream writeChar(char c)
  205. {
  206. if (out != null)
  207. out.writeChar(c);
  208. else
  209. charsetEncoder.encode(c, this);
  210. return this;
  211. }
  212. public OutStream writeChars(String s) { return writeChars(s, 0, s.length()); }
  213. public OutStream writeChars(String s, long off) { return writeChars(s, (int)off, s.length()-(int)off); }
  214. public OutStream writeChars(String s, long off, long len) { return writeChars(s, (int)off, (int)len); }
  215. public OutStream writeChars(String s, int off, int len)
  216. {
  217. int end = off+len;
  218. for (int i=off; i<end; ++i)
  219. writeChar(s.charAt(i));
  220. return this;
  221. }
  222. public OutStream print(Object obj)
  223. {
  224. String s = obj == null ? "null" : toStr(obj);
  225. return writeChars(s, 0, s.length());
  226. }
  227. public OutStream printLine() { return printLine(""); }
  228. public OutStream printLine(Object obj)
  229. {
  230. String s = obj == null ? "null" : toStr(obj);
  231. writeChars(s, 0, s.length());
  232. return writeChar('\n');
  233. }
  234. public OutStream writeObj(Object obj) { return writeObj(obj, null); }
  235. public OutStream writeObj(Object obj, Map options)
  236. {
  237. new ObjEncoder(this, options).writeObj(obj);
  238. return this;
  239. }
  240. public OutStream writeProps(Map props) { return writeProps(props, true); }
  241. public OutStream writeProps(Map props, boolean close)
  242. {
  243. Charset origCharset = charset();
  244. charset(Charset.utf8());
  245. try
  246. {
  247. List keys = props.keys();
  248. int size = keys.sz();
  249. for (int i=0; i<size; ++i)
  250. {
  251. String key = (String)keys.get(i);
  252. String val = (String)props.get(key);
  253. writePropStr(key);
  254. writeChar('=');
  255. writePropStr(val);
  256. writeChar('\n');
  257. }
  258. return this;
  259. }
  260. finally
  261. {
  262. try { if (close) close(); } catch (Exception e) { e.printStackTrace(); }
  263. charset(origCharset);
  264. }
  265. }
  266. private void writePropStr(String s)
  267. {
  268. int len = s.length();
  269. for (int i=0; i<len; ++i)
  270. {
  271. int ch = s.charAt(i);
  272. int peek = i+1<len ? s.charAt(i+1) : -1;
  273. // escape special chars
  274. switch (ch)
  275. {
  276. case '\n': writeChar('\\').writeChar('n'); continue;
  277. case '\r': writeChar('\\').writeChar('r'); continue;
  278. case '\t': writeChar('\\').writeChar('t'); continue;
  279. case '\\': writeChar('\\').writeChar('\\'); continue;
  280. }
  281. // escape control chars, comments, and =
  282. if ((ch < ' ') || (ch == '/' && (peek == '/' || peek == '*')) || (ch == '='))
  283. {
  284. long nib1 = FanInt.toDigit((ch>>4)&0xf, 16);
  285. long nib2 = FanInt.toDigit((ch>>0)&0xf, 16);
  286. this.writeChar('\\').writeChar('u')
  287. .writeChar('0').writeChar('0')
  288. .writeChar(nib1).writeChar(nib2);
  289. continue;
  290. }
  291. // normal character
  292. writeChar(ch);
  293. }
  294. }
  295. public OutStream writeXml(String s) { return writeXml(s, 0); }
  296. public OutStream writeXml(String s, long mask)
  297. {
  298. boolean escNewlines = (mask & xmlEscNewlines) != 0;
  299. boolean escQuotes = (mask & xmlEscQuotes) != 0;
  300. boolean escUnicode = (mask & xmlEscUnicode) != 0;
  301. int len = s.length();
  302. String hex = "0123456789abcdef";
  303. for (int i=0; i<len; ++i)
  304. {
  305. char ch = s.charAt(i);
  306. switch (ch)
  307. {
  308. // table switch on control chars
  309. case 0: case 1: case 2: case 3: case 4: case 5: case 6:
  310. case 7: case 8: /*case 9: case 10:*/ case 11: case 12: /*case 13:*/
  311. case 14: case 15: case 16: case 17: case 18: case 19: case 20:
  312. case 21: case 22: case 23: case 24: case 25: case 26: case 27:
  313. case 28: case 29: case 30: case 31:
  314. writeXmlEsc(ch);
  315. break;
  316. // newlines
  317. case '\n': case '\r':
  318. if (!escNewlines) writeChar(ch);
  319. else writeXmlEsc(ch);
  320. break;
  321. // space
  322. case ' ':
  323. writeChar(' ');
  324. break;
  325. // table switch on common ASCII chars
  326. case '!': case '#': case '$': case '%': case '(': case ')': case '*':
  327. case '+': case ',': case '-': case '.': case '/': case '0': case '1':
  328. case '2': case '3': case '4': case '5': case '6': case '7': case '8':
  329. case '9': case ':': case ';': case '=': case '?': case '@': case 'A':
  330. case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H':
  331. case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O':
  332. case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V':
  333. case 'W': case 'X': case 'Y': case 'Z': case '[': case '\\': case ']':
  334. case '^': case '_': case '`': case 'a': case 'b': case 'c': case 'd':
  335. case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
  336. case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
  337. case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y':
  338. case 'z': case '{': case '|': case '}': case '~':
  339. writeChar(ch);
  340. break;
  341. // XML control characters
  342. case '<':
  343. writeChar('&').writeChar('l').writeChar('t').writeChar(';');
  344. break;
  345. case '>':
  346. if (i > 0 && s.charAt(i-1) != ']') writeChar('>');
  347. else writeChar('&').writeChar('g').writeChar('t').writeChar(';');
  348. break;
  349. case '&':
  350. writeChar('&').writeChar('a').writeChar('m').writeChar('p').writeChar(';');
  351. break;
  352. case '"':
  353. if (!escQuotes) writeChar(ch);
  354. else writeChar('&').writeChar('q').writeChar('u').writeChar('o').writeChar('t').writeChar(';');
  355. break;
  356. case '\'':
  357. if (!escQuotes) writeChar(ch);
  358. else writeChar('&').writeChar('#').writeChar('3').writeChar('9').writeChar(';');
  359. break;
  360. // default
  361. default:
  362. if (ch <= 0xf7 || !escUnicode)
  363. writeChar(ch);
  364. else
  365. writeXmlEsc(ch);
  366. }
  367. }
  368. return this;
  369. }
  370. private void writeXmlEsc(int ch)
  371. {
  372. Charset.Encoder enc = charsetEncoder;
  373. String hex = "0123456789abcdef";
  374. enc.encode('&', this);
  375. enc.encode('#', this);
  376. enc.encode('x', this);
  377. if (ch > 0xff)
  378. {
  379. enc.encode(hex.charAt((ch >> 12) & 0xf), this);
  380. enc.encode(hex.charAt((ch >> 8) & 0xf), this);
  381. }
  382. enc.encode(hex.charAt((ch >> 4) & 0xf), this);
  383. enc.encode(hex.charAt((ch >> 0) & 0xf), this);
  384. enc.encode(';', this);
  385. }
  386. public static final long xmlEscNewlines = 0x01;
  387. public static final long xmlEscQuotes = 0x02;
  388. public static final long xmlEscUnicode = 0x04;
  389. public OutStream flush()
  390. {
  391. if (out != null) out.flush();
  392. return this;
  393. }
  394. public OutStream sync()
  395. {
  396. if (out != null) out.sync();
  397. return this;
  398. }
  399. public boolean close()
  400. {
  401. if (out != null) return out.close();
  402. return true;
  403. }
  404. //////////////////////////////////////////////////////////////////////////
  405. // Java Utils
  406. //////////////////////////////////////////////////////////////////////////
  407. public OutStream indent(int num)
  408. {
  409. for (int i=0; i<num; ++i)
  410. charsetEncoder.encode(' ', this);
  411. return this;
  412. }
  413. //////////////////////////////////////////////////////////////////////////
  414. // Fields
  415. //////////////////////////////////////////////////////////////////////////
  416. OutStream out;
  417. boolean bigEndian = true;
  418. Charset charset = Charset.utf8();
  419. Charset.Encoder charsetEncoder = charset.newEncoder();
  420. }