Switch to Mozilla Public Libraty with options
[loomclient.git] / java / cc / loom / LoomClient.java
blobb42aad2e29779964c4d982ceb67d5c83794c60cc
1 /*
2 * Client to the webapp at http://loom.cc/
3 * See https://loom.cc/?function=help&topic=grid_tutorial&mode=advanced
5 * Requires Java 5 generics.
6 * Port to earlier versions by removing <String,String> from the
7 * KV class definition.
8 */
10 package cc.loom;
12 import java.util.LinkedHashMap;
13 import java.util.Vector;
14 import java.util.Iterator;
15 import java.util.StringTokenizer;
16 import java.io.InputStream;
17 import java.io.InputStreamReader;
18 import java.net.URL;
19 import java.net.URLConnection;
21 /**
22 * A client interface to the webapp at http://loom.cc
24 public class LoomClient {
26 /**
27 * The beginning of the URL to access the server.
28 * Defaults to https://loom.cc/
30 String url_prefix; // Who you gonna call?
32 /**
33 * No-arg constructor. Defaults url_prefix to https://loom.cc/
35 public LoomClient() {
36 this("https://loom.cc/");
39 /**
40 * Constructor to explicitly specify the url_prefix.
41 * @param prefix The url_prefix
43 public LoomClient(String prefix) {
44 if (!prefix.endsWith("/")) prefix = prefix + "/";
45 url_prefix = prefix;
48 /**
49 * This is all you really need to call.
50 * The functions below are just syntactic sugar for calling get()
51 * @param keys Map argument name strings to their value strings
53 public KV get(KV keys) {
54 return get(keys, null);
57 /**
58 * Implementation of get(KV) and debugging version
59 * @param keys Map argument name strings to their value strings
60 * @param urlv index 0 filled on output with the URL string
62 public KV get(KV keys, String[] urlv) {
63 String url = url(keys);
64 if (urlv != null) urlv[0] = url;
65 String kv = fetchURLString(url);
66 return parsekv(kv);
69 /**
70 * Buy a grid location
71 * @param type the asset type, an ID (32 character hex string)
72 * @param location the location to buy, an ID
73 * @param usage the usage location to debit, an ID
75 public KV buy(String type, String location, String usage) {
76 return buy(type, location, usage, null);
79 /**
80 * Buy a grid location, implementation and debugging version
81 * @param type the asset type, an ID (32 character hex string)
82 * @param location the location to buy, an ID
83 * @param usage the usage location to debit, an ID
84 * @param urlv index 0 filled on output with the URL string
86 public KV buy(String type, String location, String usage, String[] urlv) {
87 KV keys = new KV();
88 keys.put("function", "grid");
89 keys.put("action", "buy");
90 keys.put("type", type);
91 keys.put("loc", location);
92 keys.put("usage", usage);
93 return get(keys, urlv);
96 /**
97 * Sell a grid location
98 * @param type the asset type, an ID (32 character hex string)
99 * @param location the location to buy, an ID
100 * @param usage the usage location to debit, an ID
102 public KV sell(String type, String location, String usage) {
103 return sell(type, location, usage, null);
107 * Sell a grid location - implementation and debugging version
108 * @param type the asset type, an ID (32 character hex string)
109 * @param location the location to buy, an ID
110 * @param usage the usage location to debit, an ID
111 * @param urlv index 0 filled on output with the URL string
113 public KV sell(String type, String location, String usage, String[] urlv) {
114 KV keys = new KV();
115 keys.put("function", "grid");
116 keys.put("action", "sell");
117 keys.put("type", type);
118 keys.put("loc", location);
119 keys.put("usage", usage);
120 return get(keys, urlv);
124 * Change the issuer of a grid location, implementation and debugging version
125 * @param type the asset type, an ID (32 character hex string)
126 * @param orig the origin issuer location, an ID
127 * @param dest the destination issuer location, an ID
129 public KV issuer(String type, String orig, String dest) {
130 return issuer(type, orig, dest, null);
134 * Change the issuer of a grid location, implementation and debugging version
135 * @param type the asset type, an ID (32 character hex string)
136 * @param orig the origin issuer location, an ID
137 * @param dest the destination issuer location, an ID
138 * @param urlv index 0 filled on output with the URL string
140 public KV issuer(String type, String orig, String dest, String[] urlv) {
141 KV keys = new KV();
142 keys.put("function", "grid");
143 keys.put("action", "issuer");
144 keys.put("type", type);
145 keys.put("orig", orig);
146 keys.put("dest", dest);
147 return get(keys, urlv);
151 * Return the value of a grid location
152 * @param type the asset type, an ID (32 character hex string)
153 * @param location the location to read
155 public KV touch(String type, String location) {
156 return touch(type, location, null);
160 * Return the value of a grid location, implementation and debugging version
161 * @param type the asset type, an ID (32 character hex string)
162 * @param location the location to read, an ID
163 * @param urlv index 0 filled on output with the URL string
165 public KV touch(String type, String location, String[] urlv) {
166 KV keys = new KV();
167 keys.put("function", "grid");
168 keys.put("action", "touch");
169 keys.put("type", type);
170 keys.put("loc", location);
171 return get(keys, urlv);
175 * Return the value of a grid location
176 * @param type the asset type, an ID (32 character hex string)
177 * @param hash the hash of the location to read, a 64 character hex string
179 public KV look(String type, String hash) {
180 return look(type, hash, null);
184 * Return the value of a grid location, implementation and debugging version
185 * @param type the asset type, an ID (32 character hex string)
186 * @param hash the hash of the location to read, a 64 character hex string
187 * @param urlv index 0 filled on output with the URL string
189 public KV look(String type, String hash, String[] urlv) {
190 KV keys = new KV();
191 keys.put("function", "grid");
192 keys.put("action", "look");
193 keys.put("type", type);
194 keys.put("hash", hash);
195 return get(keys, urlv);
199 * Move assets from one location to another
200 * @param type the asset type, an ID (32 character hex string)
201 * @param qty The quantity to move, a string of decimal digits
202 * @param orig the location to move from, an ID
203 * @param dest the location to move to, an ID
205 public KV move(String type, String qty, String orig, String dest) {
206 return move(type, qty, orig, dest, null);
210 * Move assets from one location to another, implementation and debugging version
211 * @param type the asset type, an ID (32 character hex string)
212 * @param qty The quantity to move, a string of decimal digits
213 * @param orig the location to move from, an ID
214 * @param dest the location to move to, an ID
215 * @param urlv index 0 filled on output with the URL string
217 public KV move(String type, String qty, String orig, String dest, String[] urlv) {
218 KV keys = new KV();
219 keys.put("function", "grid");
220 keys.put("action", "move");
221 keys.put("type", type);
222 keys.put("qty", qty);
223 keys.put("orig", orig);
224 keys.put("dest", dest);
225 return get(keys, urlv);
229 * Archive functions
233 * Buy an archive location
234 * @param loc The location to buy
235 * @param usage The location of at least one usage token with which to pay
237 public KV buyArchive(String loc, String usage) {
238 return buyArchive(loc, usage, null);
242 * Buy an archive location, implementation and debugging version
243 * @param loc The location to buy
244 * @param usage The location of at least one usage token with which to pay
245 * @param urlv index 0 filled on output with the URL string
247 public KV buyArchive(String loc, String usage, String[] urlv) {
248 KV keys = new KV();
249 keys.put("function", "archive");
250 keys.put("action", "buy");
251 keys.put("loc", loc);
252 keys.put("usage", usage);
253 return get(keys, urlv);
257 * Sell an archive location.
258 * The location must be empty. Write "" to it before selling.
259 * @param loc The location to sell
260 * @param usage The location into which to put a usage token
262 public KV sellArchive(String loc, String usage) {
263 return sellArchive(loc, usage, null);
267 * Sell an archive location, implementation and debugging version
268 * The location must be empty. Write "" to it before selling.
269 * @param loc The location to sell
270 * @param usage The location into which to put a usage token.
271 * @param urlv index 0 filled on output with the URL string
273 public KV sellArchive(String loc, String usage, String[] urlv) {
274 KV keys = new KV();
275 keys.put("function", "archive");
276 keys.put("action", "sell");
277 keys.put("loc", loc);
278 keys.put("usage", usage);
279 return get(keys, urlv);
283 * Touch an archive location.
284 * @param loc The location to touch
286 public KV touchArchive(String loc) {
287 return touchArchive(loc, null);
291 * Touch an archive location, implementation and debugging version
292 * @param loc The location to touch
293 * @param urlv index 0 filled on output with the URL string
295 public KV touchArchive(String loc, String[] urlv) {
296 KV keys = new KV();
297 keys.put("function", "archive");
298 keys.put("action", "touch");
299 keys.put("loc", loc);
300 return get(keys, urlv);
304 * Look at an archive location, using its location's hash
305 * @param hash The hash of the location to look at.
307 public KV lookArchive(String hash) {
308 return lookArchive(hash, null);
312 * Look at an archive location, using its location's hash.
313 * Implementation and debugging version.
314 * @param hash The hash of the location to look at.
315 * @param urlv index 0 filled on output with the URL string
317 public KV lookArchive(String hash, String[] urlv) {
318 KV keys = new KV();
319 keys.put("function", "archive");
320 keys.put("action", "look");
321 keys.put("hash", hash);
322 return get(keys, urlv);
326 * Write at an archive location.
327 * @param loc The location to write
328 * @param usage The location of 1 usage token per 16 bytes written (delta from old value).
329 * @param content The content to write
331 public KV writeArchive(String loc, String usage, String content) {
332 return writeArchive(loc, usage, content, null);
336 * Write at an archive location, implementation and debugging version.
337 * @param loc The location to write
338 * @param usage The location of 1 usage token per 16 bytes written (delta from old value).
339 * @param content The content to write
341 public KV writeArchive(String loc, String usage, String content, String[] urlv) {
342 KV keys = new KV();
343 keys.put("function", "archive");
344 keys.put("action", "write");
345 keys.put("loc", loc);
346 keys.put("usage", usage);
347 keys.put("content", content);
348 return get(keys, urlv);
352 * Return the URL corresponding to a set of keys
353 * @param keys the keys
355 public String url(KV keys) {
356 StringBuffer str = new StringBuffer(url_prefix);
357 String delim = "?";
358 Iterator i = keys.keySet().iterator();
359 while (i.hasNext()) {
360 String key = (String)i.next();
361 String value = (String)keys.get(key);
362 str.append(delim).append(key).append("=").append(urlencode(value));
363 delim = "&";
365 return str.toString();
369 * Parse the KV string returned by Loom
370 * @param kv The KV string
371 * @return null if there is no opening left paren on the first line.
373 // This needs to un-c-code the return.
374 // e.g. "\n" -> newline
375 public static KV parsekv(String kv) {
376 StringTokenizer tok = new StringTokenizer(kv, "\n");
377 boolean first = true;
378 KV res = null;
379 String key = "";
380 String value = "";
381 while(tok.hasMoreTokens()) {
382 String line = tok.nextToken();
383 if (first) {
384 if (!line.equals("(")) return res;
385 res = new KV();
387 first = false;
388 if (line.equals(")")) return res;
389 if (line.charAt(0) == ':') key = line.substring(1);
390 else if (line.charAt(0) == '=') {
391 value = unquoteCString(line.substring(1));
392 res.put(key, value);
395 return res;
399 * CQuote a string. Opposite of unquoteCstring()
400 * @param cstring the string to quote
402 public static String quoteCString(String cstring) {
403 StringBuffer buf = new StringBuffer();
404 for (int i=0; i<cstring.length(); i++) {
405 char chr = cstring.charAt(i);
406 if (chr == '\n') buf.append("\\n");
407 else if (chr == '"') buf.append("\\\"");
408 else if (chr == '\t') buf.append("\\t");
409 else if (chr == '\\') buf.append("\\\\");
410 else if (chr < ' ' || chr > '~') {
411 buf.append('\\').append(String.format("%03o", (int)chr));
412 } else buf.append(chr);
414 return buf.toString();
418 * Un-CQuote a string. The opposite of quoteCString()
419 * @param cstring The string to unquote.
421 public static String unquoteCString(String cstring) {
422 StringBuffer buf = new StringBuffer();
423 int len = cstring.length();
424 int i = 0;
425 while (i < len) {
426 char chr = cstring.charAt(i);
427 if (chr == '\\') {
428 i++;
429 if (i >= len) {
430 buf.append(chr);
431 break;
433 chr = cstring.charAt(i);
434 if (chr == 'n') buf.append('\n');
435 else if (chr == '"') buf.append('"');
436 else if (chr == 't') buf.append('\t');
437 else if (chr == '\\') buf.append('\\');
438 else if (chr >= '0' && chr <= '9') {
439 if (len < (i + 3)) {
440 buf.append(cstring.substring(i-1));
441 break;
443 buf.append((char)Integer.parseInt(cstring.substring(i, i+3), 8));
444 i += 2;
445 } else buf.append('\\').append(chr);
446 } else {
447 buf.append(chr);
449 i++;
451 return buf.toString();
455 * Fetch the contents of a URL as a string
457 public static String fetchURLString(String address) {
458 //println("URL: " + address);
459 char[] buf = new char[4096];
460 try {
461 URL url = new URL(address);
462 URLConnection connection = url.openConnection();
463 InputStreamReader in = new InputStreamReader(connection.getInputStream());
464 StringBuffer all = new StringBuffer();
465 int size = 0;
466 while(size >= 0) {
467 size = in.read(buf, 0, 4096);
468 if (size > 0) all.append(buf, 0, size);
470 //println(all.toString());
471 return all.toString();
472 } catch (Exception e) {
473 return "()";
478 * Encode a string for a URL
480 public static String urlencode(String str) {
481 try {
482 return java.net.URLEncoder.encode(str, "UTF-8");
483 } catch (java.io.UnsupportedEncodingException e) {
484 return str;
489 * I don't like typing System.out.println
491 public static void println(String line) {
492 System.out.println(line);
496 * Print the usage for the command line app
498 public static void usage() {
499 println("Usage: LoomClient function args...");
500 println(" buy type location usage");
501 println(" sell type location usage");
502 println(" issuer type orig dest");
503 println(" touch type location");
504 println(" look type hash");
505 println(" move type qty orig dest");
506 println(" buyarch loc usage");
507 println(" sellarch loc usage");
508 println(" toucharch loc");
509 println(" lookarch hash");
510 println(" writearch loc usage content");
514 * Print a key/value table to stdout
516 public static void printKV(KV kv) {
517 Iterator keys = kv.keySet().iterator();
518 println("(");
519 while(keys.hasNext()) {
520 String key = (String)keys.next();
521 String value = kv.get(key);
522 println(":" + quoteCString(key));
523 println("=" + quoteCString(value));
525 println(")");
529 * A little command line example of using the library.
531 public static void main(String[] args) {
532 if (args.length < 2) {
533 usage(); return;
535 String function = args[0];
536 LoomClient client = new LoomClient();
537 String[] urlv = new String[1];
538 KV kv = null;
539 if (function.equals("buy") || function.equals("sell")) {
540 if (args.length != 4) {
541 usage(); return;
543 String type = args[1];
544 String location = args[2];
545 String usage = args[3];
546 kv = function.equals("buy") ?
547 client.buy(type, location, usage, urlv) :
548 client.sell(type, location, usage, urlv);
549 } else if (function.equals("issuer")) {
550 if (args.length != 4) {
551 usage(); return;
553 String type = args[1];
554 String orig = args[2];
555 String dest = args[3];
556 kv = client.issuer(type, orig, dest, urlv);
557 } else if (function.equals("touch")) {
558 if (args.length != 3) {
559 usage(); return;
561 String type = args[1];
562 String location = args[2];
563 kv = client.touch(type, location, urlv);
564 } else if (function.equals("look")) {
565 if (args.length != 3) {
566 usage(); return;
568 String type = args[1];
569 String hash = args[2];
570 kv = client.look(type, hash, urlv);
571 } else if (function.equals("move")) {
572 if (args.length != 5) {
573 usage(); return;
575 String type = args[1];
576 String qty = args[2];
577 String orig = args[3];
578 String dest = args[4];
579 kv = client.move(type, qty, orig, dest, urlv);
580 } else if (function.equals("buyarch")) {
581 if (args.length != 3) {
582 usage(); return;
584 String loc = args[1];
585 String usage = args[2];
586 kv = client.buyArchive(loc, usage, urlv);
587 } else if (function.equals("sellarch")) {
588 if (args.length != 3) {
589 usage(); return;
591 String loc = args[1];
592 String usage = args[2];
593 kv = client.sellArchive(loc, usage, urlv);
594 } else if (function.equals("toucharch")) {
595 if (args.length != 2) {
596 usage(); return;
598 String loc = args[1];
599 kv = client.touchArchive(loc, urlv);
600 } else if (function.equals("lookarch")) {
601 if (args.length != 2) {
602 usage(); return;
604 String hash = args[1];
605 kv = client.lookArchive(hash, urlv);
606 } else if (function.equals("writearch")) {
607 if (args.length != 4) {
608 usage(); return;
610 String loc = args[1];
611 String usage = args[2];
612 String content = args[3];
613 kv = client.writeArchive(loc, usage, content, urlv);
614 } else {
615 usage();
617 if (kv != null) {
618 if (urlv[0] != null) {
619 println(urlv[0]);
621 printKV(kv);
627 * A LinkedHashMap that maps strings to strings
629 public static class KV extends LinkedHashMap<String,String> {
630 public KV() {
631 super();
634 public String get(String key) {
635 return super.get(key);
638 public String put(String key, String value) {
639 return super.put(key, value);
645 /* ***** BEGIN LICENSE BLOCK *****
646 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/Apache 2.0
648 * The contents of this file are subject to the Mozilla Public License Version
649 * 1.1 (the "License"); you may not use this file except in compliance with
650 * the License. You may obtain a copy of the License at
651 * http://www.mozilla.org/MPL/
653 * Software distributed under the License is distributed on an "AS IS" basis,
654 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
655 * for the specific language governing rights and limitations under the
656 * License.
658 * The Original Code is Trubanc.com
660 * The Initial Developer of the Original Code is
661 * Bill St. Clair.
662 * Portions created by the Initial Developer are Copyright (C) 2008
663 * the Initial Developer. All Rights Reserved.
665 * Contributor(s):
666 * Bill St. Clair <bill@billstclair.com>
668 * Alternatively, the contents of this file may be used under the
669 * terms of the GNU General Public License Version 2 or later (the
670 * "GPL"), the GNU Lesser General Public License Version 2.1 or later
671 * (the "LGPL"), or The Apache License Version 2.0 (the "AL"), in
672 * which case the provisions of the GPL, LGPL, or AL are applicable
673 * instead of those above. If you wish to allow use of your version of
674 * this file only under the terms of the GPL, the LGPL, or the AL, and
675 * not to allow others to use your version of this file under the
676 * terms of the MPL, indicate your decision by deleting the provisions
677 * above and replace them with the notice and other provisions
678 * required by the GPL or the LGPL. If you do not delete the
679 * provisions above, a recipient may use your version of this file
680 * under the terms of any one of the MPL, the GPL the LGPL, or the AL.
681 ****** END LICENSE BLOCK ***** */