| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- // Copyright 2007 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); You may not
- // use this file except in compliance with the License. You may obtain a copy of
- // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
- // applicable law or agreed to in writing, software distributed under the
- // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
- // OF ANY KIND, either express or implied. See the License for the specific
- // language governing permissions and limitations under the License.
- package com.google.scrollview;
- import com.google.scrollview.events.SVEvent;
- import com.google.scrollview.ui.SVImageHandler;
- import com.google.scrollview.ui.SVWindow;
- import org.piccolo2d.nodes.PImage;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintStream;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.ArrayList;
- import java.util.regex.Pattern;
- /**
- * The ScrollView class is the main class which gets started from the command
- * line. It sets up LUA and handles the network processing.
- * @author wanke@google.com
- */
- public class ScrollView {
- /** The port our server listens at. */
- public static int SERVER_PORT = 8461;
- /**
- * All SVWindow objects share the same connection stream. The socket is needed
- * to detect when the connection got closed, in/out are used to send and
- * receive messages.
- */
- private static Socket socket;
- private static PrintStream out;
- public static BufferedReader in;
- public static float polylineXCoords[]; // The coords being received.
- public static float polylineYCoords[]; // The coords being received.
- public static int polylineSize; // The size of the coords arrays.
- public static int polylineScanned; // The size read so far.
- private static ArrayList<SVWindow> windows; // The id to SVWindow map.
- private static Pattern intPattern; // For checking integer arguments.
- private static Pattern floatPattern; // For checking float arguments.
- /** Keeps track of the number of messages received. */
- static int nrInputLines = 0;
- /** Prints all received messages to the console if true. */
- static boolean debugViewNetworkTraffic = false;
- /** Add a new message to the outgoing queue. */
- public static void addMessage(SVEvent e) {
- if (debugViewNetworkTraffic) {
- System.out.println("(S->c) " + e.toString());
- }
- String str = e.toString();
- // Send the whole thing as UTF8.
- try {
- byte [] utf8 = str.getBytes("UTF8");
- out.write(utf8, 0, utf8.length);
- } catch (java.io.UnsupportedEncodingException ex) {
- System.out.println("Oops... can't encode to UTF8... Exiting");
- System.exit(0);
- }
- out.println();
- // Flush the output and check for errors.
- boolean error = out.checkError();
- if (error) {
- System.out.println("Connection error. Quitting ScrollView Server...");
- System.exit(0);
- }
- }
- /** Read one message from client (assuming there are any). */
- public static String receiveMessage() throws IOException {
- return in.readLine();
- }
- /**
- * The main program loop. Basically loops through receiving messages and
- * processing them and then sending messages (if there are any).
- */
- private static void IOLoop() {
- String inputLine;
- try {
- while (!socket.isClosed() && !socket.isInputShutdown() &&
- !socket.isOutputShutdown() &&
- socket.isConnected() && socket.isBound()) {
- inputLine = receiveMessage();
- if (inputLine == null) {
- // End of stream reached.
- break;
- }
- nrInputLines++;
- if (debugViewNetworkTraffic) {
- System.out.println("(c->S," + nrInputLines + ")" + inputLine);
- }
- if (polylineSize > polylineScanned) {
- // We are processing a polyline.
- // Read pairs of coordinates separated by commas.
- boolean first = true;
- for (String coordStr : inputLine.split(",")) {
- int coord = Integer.parseInt(coordStr);
- if (first) {
- polylineXCoords[polylineScanned] = coord;
- } else {
- polylineYCoords[polylineScanned++] = coord;
- }
- first = !first;
- }
- assert first;
- } else {
- // Process this normally.
- processInput(inputLine);
- }
- }
- }
- // Some connection error
- catch (IOException e) {
- System.out.println("Connection error. Quitting ScrollView Server...");
- }
- System.exit(0);
- }
- // Parse a comma-separated list of arguments into ArrayLists of the
- // possible types. Each type is stored in order, but the order
- // distinction between types is lost.
- // Note that the format is highly constrained to what the client used
- // to send to LUA:
- // Quoted string -> String.
- // true or false -> Boolean.
- // %f format number -> Float (no %e allowed)
- // Sequence of digits -> Integer
- // Nothing else allowed.
- private static void parseArguments(String argList,
- ArrayList<Integer> intList,
- ArrayList<Float> floatList,
- ArrayList<String> stringList,
- ArrayList<Boolean> boolList) {
- // str is only non-null if an argument starts with a single or double
- // quote. str is set back to null on completion of the string with a
- // matching quote. If the string contains a comma then str will stay
- // non-null across multiple argStr values until a matching closing quote.
- // Backslash escaped quotes do not count as terminating the string.
- String str = null;
- for (String argStr : argList.split(",")) {
- if (str != null) {
- // Last string was incomplete. Append argStr to it and restore comma.
- // Execute str += "," + argStr in Java.
- int length = str.length() + 1 + argStr.length();
- StringBuilder appended = new StringBuilder(length);
- appended.append(str);
- appended.append(",");
- appended.append(argStr);
- str = appended.toString();
- } else if (argStr.length() == 0) {
- continue;
- } else {
- char quote = argStr.charAt(0);
- // If it begins with a quote then it is a string, but may not
- // end this time if it contained a comma.
- if (quote == '\'' || quote == '"') {
- str = argStr;
- }
- }
- if (str != null) {
- // It began with a quote. Check that it still does.
- assert str.charAt(0) == '\'' || str.charAt(0) == '"';
- int len = str.length();
- if (len > 1 && str.charAt(len - 1) == str.charAt(0)) {
- // We have an ending quote of the right type. Now check that
- // it is not escaped. Must have an even number of slashes before.
- int slash = len - 1;
- while (slash > 0 && str.charAt(slash - 1) == '\\')
- --slash;
- if ((len - 1 - slash) % 2 == 0) {
- // It is now complete. Chop off the quotes and save.
- // TODO(rays) remove the first backslash of each pair.
- stringList.add(str.substring(1, len - 1));
- str = null;
- }
- }
- // If str is not null here, then we have a string with a comma in it.
- // Append, and the next argument at the next iteration, but check
- // that str is null after the loop terminates in case it was an
- // unterminated string.
- } else if (floatPattern.matcher(argStr).matches()) {
- // It is a float.
- floatList.add(Float.parseFloat(argStr));
- } else if (argStr.equals("true")) {
- boolList.add(true);
- } else if (argStr.equals("false")) {
- boolList.add(false);
- } else if (intPattern.matcher(argStr).matches()) {
- // Only contains digits so must be an int.
- intList.add(Integer.parseInt(argStr));
- }
- // else ignore all incompatible arguments for forward compatibility.
- }
- // All strings must have been terminated.
- assert str == null;
- }
- /** Executes the LUA command parsed as parameter. */
- private static void processInput(String inputLine) {
- if (inputLine == null) {
- return;
- }
- // Execute a function encoded as a LUA statement! Yuk!
- if (inputLine.charAt(0) == 'w') {
- // This is a method call on a window. Parse it.
- String noWLine = inputLine.substring(1);
- String[] idStrs = noWLine.split("[ :]", 2);
- int windowID = Integer.parseInt(idStrs[0]);
- // Find the parentheses.
- int start = inputLine.indexOf('(');
- int end = inputLine.lastIndexOf(')');
- // Parse the args.
- ArrayList<Integer> intList = new ArrayList<Integer>(4);
- ArrayList<Float> floatList = new ArrayList<Float>(2);
- ArrayList<String> stringList = new ArrayList<String>(4);
- ArrayList<Boolean> boolList = new ArrayList<Boolean>(3);
- parseArguments(inputLine.substring(start + 1, end),
- intList, floatList, stringList, boolList);
- int colon = inputLine.indexOf(':');
- if (colon > 1 && colon < start) {
- // This is a regular function call. Look for the name and call it.
- String func = inputLine.substring(colon + 1, start);
- if (func.equals("drawLine")) {
- windows.get(windowID).drawLine(intList.get(0), intList.get(1),
- intList.get(2), intList.get(3));
- } else if (func.equals("createPolyline")) {
- windows.get(windowID).createPolyline(intList.get(0));
- } else if (func.equals("drawPolyline")) {
- windows.get(windowID).drawPolyline();
- } else if (func.equals("drawRectangle")) {
- windows.get(windowID).drawRectangle(intList.get(0), intList.get(1),
- intList.get(2), intList.get(3));
- } else if (func.equals("setVisible")) {
- windows.get(windowID).setVisible(boolList.get(0));
- } else if (func.equals("setAlwaysOnTop")) {
- windows.get(windowID).setAlwaysOnTop(boolList.get(0));
- } else if (func.equals("addMessage")) {
- windows.get(windowID).addMessage(stringList.get(0));
- } else if (func.equals("addMessageBox")) {
- windows.get(windowID).addMessageBox();
- } else if (func.equals("clear")) {
- windows.get(windowID).clear();
- } else if (func.equals("setStrokeWidth")) {
- windows.get(windowID).setStrokeWidth(floatList.get(0));
- } else if (func.equals("drawEllipse")) {
- windows.get(windowID).drawEllipse(intList.get(0), intList.get(1),
- intList.get(2), intList.get(3));
- } else if (func.equals("pen")) {
- if (intList.size() == 4) {
- windows.get(windowID).pen(intList.get(0), intList.get(1),
- intList.get(2), intList.get(3));
- } else {
- windows.get(windowID).pen(intList.get(0), intList.get(1),
- intList.get(2));
- }
- } else if (func.equals("brush")) {
- if (intList.size() == 4) {
- windows.get(windowID).brush(intList.get(0), intList.get(1),
- intList.get(2), intList.get(3));
- } else {
- windows.get(windowID).brush(intList.get(0), intList.get(1),
- intList.get(2));
- }
- } else if (func.equals("textAttributes")) {
- windows.get(windowID).textAttributes(stringList.get(0),
- intList.get(0),
- boolList.get(0),
- boolList.get(1),
- boolList.get(2));
- } else if (func.equals("drawText")) {
- windows.get(windowID).drawText(intList.get(0), intList.get(1),
- stringList.get(0));
- } else if (func.equals("addMenuBarItem")) {
- if (boolList.size() > 0) {
- windows.get(windowID).addMenuBarItem(stringList.get(0),
- stringList.get(1),
- intList.get(0),
- boolList.get(0));
- } else if (intList.size() > 0) {
- windows.get(windowID).addMenuBarItem(stringList.get(0),
- stringList.get(1),
- intList.get(0));
- } else {
- windows.get(windowID).addMenuBarItem(stringList.get(0),
- stringList.get(1));
- }
- } else if (func.equals("addPopupMenuItem")) {
- if (stringList.size() == 4) {
- windows.get(windowID).addPopupMenuItem(stringList.get(0),
- stringList.get(1),
- intList.get(0),
- stringList.get(2),
- stringList.get(3));
- } else {
- windows.get(windowID).addPopupMenuItem(stringList.get(0),
- stringList.get(1));
- }
- } else if (func.equals("update")) {
- windows.get(windowID).update();
- } else if (func.equals("showInputDialog")) {
- windows.get(windowID).showInputDialog(stringList.get(0));
- } else if (func.equals("showYesNoDialog")) {
- windows.get(windowID).showYesNoDialog(stringList.get(0));
- } else if (func.equals("zoomRectangle")) {
- windows.get(windowID).zoomRectangle(intList.get(0), intList.get(1),
- intList.get(2), intList.get(3));
- } else if (func.equals("readImage")) {
- PImage image = SVImageHandler.readImage(intList.get(2), in);
- windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
- } else if (func.equals("drawImage")) {
- PImage image = new PImage(stringList.get(0));
- windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
- } else if (func.equals("destroy")) {
- windows.get(windowID).destroy();
- }
- // else for forward compatibility purposes, silently ignore any
- // unrecognized function call.
- } else {
- // No colon. Check for create window.
- if (idStrs[1].startsWith("= luajava.newInstance")) {
- while (windows.size() <= windowID) {
- windows.add(null);
- }
- windows.set(windowID, new SVWindow(stringList.get(1),
- intList.get(0), intList.get(1),
- intList.get(2), intList.get(3),
- intList.get(4), intList.get(5),
- intList.get(6)));
- }
- // else for forward compatibility purposes, silently ignore any
- // unrecognized function call.
- }
- } else if (inputLine.startsWith("svmain")) {
- // Startup or end. Startup is a lua bind, which is now a no-op.
- if (inputLine.startsWith("svmain:exit")) {
- exit();
- }
- // else for forward compatibility purposes, silently ignore any
- // unrecognized function call.
- }
- // else for forward compatibility purposes, silently ignore any
- // unrecognized function call.
- }
- /** Called from the client to make the server exit. */
- public static void exit() {
- System.exit(0);
- }
- /**
- * The main function. Sets up LUA and the server connection and then calls the
- * IOLoop.
- */
- public static void main(String[] args) {
- if (args.length > 0) {
- SERVER_PORT = Integer.parseInt(args[0]);
- }
- windows = new ArrayList<SVWindow>(100);
- intPattern = Pattern.compile("[0-9-][0-9]*");
- floatPattern = Pattern.compile("[0-9-][0-9]*\\.[0-9]*");
- // Open a socket to listen on.
- try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) {
- System.out.println("Socket started on port " + SERVER_PORT);
- // Wait (blocking) for an incoming connection
- socket = serverSocket.accept();
- System.out.println("Client connected");
- // Setup the streams
- out = new PrintStream(socket.getOutputStream(), true, "UTF-8");
- in =
- new BufferedReader(new InputStreamReader(socket.getInputStream(),
- "UTF8"));
- } catch (IOException e) {
- // Something went wrong and we were unable to set up a connection. This is
- // pretty much a fatal error.
- // Note: The server does not get restarted automatically if this happens.
- e.printStackTrace();
- System.exit(1);
- }
- // Enter the main program loop.
- IOLoop();
- }
- }
|