All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ScrollView.java
Go to the documentation of this file.
1 // Copyright 2007 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); You may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
6 // applicable law or agreed to in writing, software distributed under the
7 // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
8 // OF ANY KIND, either express or implied. See the License for the specific
9 // language governing permissions and limitations under the License.
10 
11 package com.google.scrollview;
12 
16 import org.piccolo2d.nodes.PImage;
17 
18 import java.io.BufferedReader;
19 import java.io.IOException;
20 import java.io.InputStreamReader;
21 import java.io.PrintStream;
22 import java.net.ServerSocket;
23 import java.net.Socket;
24 import java.util.ArrayList;
25 import java.util.regex.Pattern;
26 
32 public class ScrollView {
33 
35  public static int SERVER_PORT = 8461;
36 
42  private static Socket socket;
43  private static PrintStream out;
44  public static BufferedReader in;
45  public static float polylineXCoords[]; // The coords being received.
46  public static float polylineYCoords[]; // The coords being received.
47  public static int polylineSize; // The size of the coords arrays.
48  public static int polylineScanned; // The size read so far.
49  private static ArrayList<SVWindow> windows; // The id to SVWindow map.
50  private static Pattern intPattern; // For checking integer arguments.
51  private static Pattern floatPattern; // For checking float arguments.
52 
54  static int nrInputLines = 0;
55 
57  static boolean debugViewNetworkTraffic = false;
58 
60  public static void addMessage(SVEvent e) {
61  if (debugViewNetworkTraffic) {
62  System.out.println("(S->c) " + e.toString());
63  }
64  String str = e.toString();
65  // Send the whole thing as UTF8.
66  try {
67  byte [] utf8 = str.getBytes("UTF8");
68  out.write(utf8, 0, utf8.length);
69  } catch (java.io.UnsupportedEncodingException ex) {
70  System.out.println("Oops... can't encode to UTF8... Exiting");
71  System.exit(0);
72  }
73  out.println();
74  // Flush the output and check for errors.
75  boolean error = out.checkError();
76  if (error) {
77  System.out.println("Connection error. Quitting ScrollView Server...");
78  System.exit(0);
79  }
80  }
81 
83  public static String receiveMessage() throws IOException {
84  return in.readLine();
85  }
86 
91  private static void IOLoop() {
92  String inputLine;
93 
94  try {
95  while (!socket.isClosed() && !socket.isInputShutdown() &&
96  !socket.isOutputShutdown() &&
97  socket.isConnected() && socket.isBound()) {
98  inputLine = receiveMessage();
99  nrInputLines++;
100  if (debugViewNetworkTraffic) {
101  System.out.println("(c->S," + nrInputLines + ")" + inputLine);
102  }
103 
104  if (polylineSize > polylineScanned) {
105  // We are processing a polyline.
106  // Read pairs of coordinates separated by commas.
107  boolean first = true;
108  for (String coordStr : inputLine.split(",")) {
109  int coord = Integer.parseInt(coordStr);
110  if (first) {
111  polylineXCoords[polylineScanned] = coord;
112  } else {
113  polylineYCoords[polylineScanned++] = coord;
114  }
115  first = !first;
116  }
117  assert first;
118  } else {
119  // Process this normally.
120  processInput(inputLine);
121  }
122  }
123  }
124  // Some connection error
125  catch (IOException e) {
126  System.out.println("Connection error. Quitting ScrollView Server...");
127  }
128  System.exit(0);
129  }
130 
131  // Parse a comma-separated list of arguments into ArrayLists of the
132  // possible types. Each type is stored in order, but the order
133  // distinction between types is lost.
134  // Note that the format is highly constrained to what the client used
135  // to send to LUA:
136  // Quoted string -> String.
137  // true or false -> Boolean.
138  // %f format number -> Float (no %e allowed)
139  // Sequence of digits -> Integer
140  // Nothing else allowed.
141  private static void parseArguments(String argList,
142  ArrayList<Integer> intList,
143  ArrayList<Float> floatList,
144  ArrayList<String> stringList,
145  ArrayList<Boolean> boolList) {
146  // str is only non-null if an argument starts with a single or double
147  // quote. str is set back to null on completion of the string with a
148  // matching quote. If the string contains a comma then str will stay
149  // non-null across multiple argStr values until a matching closing quote.
150  // Backslash escaped quotes do not count as terminating the string.
151  String str = null;
152  for (String argStr : argList.split(",")) {
153  if (str != null) {
154  // Last string was incomplete. Append argStr to it and restore comma.
155  // Execute str += "," + argStr in Java.
156  int length = str.length() + 1 + argStr.length();
157  StringBuilder appended = new StringBuilder(length);
158  appended.append(str);
159  appended.append(",");
160  appended.append(argStr);
161  str = appended.toString();
162  } else if (argStr.length() == 0) {
163  continue;
164  } else {
165  char quote = argStr.charAt(0);
166  // If it begins with a quote then it is a string, but may not
167  // end this time if it contained a comma.
168  if (quote == '\'' || quote == '"') {
169  str = argStr;
170  }
171  }
172  if (str != null) {
173  // It began with a quote. Check that it still does.
174  assert str.charAt(0) == '\'' || str.charAt(0) == '"';
175  int len = str.length();
176  if (len > 1 && str.charAt(len - 1) == str.charAt(0)) {
177  // We have an ending quote of the right type. Now check that
178  // it is not escaped. Must have an even number of slashes before.
179  int slash = len - 1;
180  while (slash > 0 && str.charAt(slash - 1) == '\\')
181  --slash;
182  if ((len - 1 - slash) % 2 == 0) {
183  // It is now complete. Chop off the quotes and save.
184  // TODO(rays) remove the first backslash of each pair.
185  stringList.add(str.substring(1, len - 1));
186  str = null;
187  }
188  }
189  // If str is not null here, then we have a string with a comma in it.
190  // Append , and the next argument at the next iteration, but check
191  // that str is null after the loop terminates in case it was an
192  // unterminated string.
193  } else if (floatPattern.matcher(argStr).matches()) {
194  // It is a float.
195  floatList.add(Float.parseFloat(argStr));
196  } else if (argStr.equals("true")) {
197  boolList.add(true);
198  } else if (argStr.equals("false")) {
199  boolList.add(false);
200  } else if (intPattern.matcher(argStr).matches()) {
201  // Only contains digits so must be an int.
202  intList.add(Integer.parseInt(argStr));
203  }
204  // else ignore all incompatible arguments for forward compatibility.
205  }
206  // All strings must have been terminated.
207  assert str == null;
208  }
209 
211  private static void processInput(String inputLine) {
212  if (inputLine == null) {
213  return;
214  }
215  // Execute a function encoded as a LUA statement! Yuk!
216  if (inputLine.charAt(0) == 'w') {
217  // This is a method call on a window. Parse it.
218  String noWLine = inputLine.substring(1);
219  String[] idStrs = noWLine.split("[ :]", 2);
220  int windowID = Integer.parseInt(idStrs[0]);
221  // Find the parentheses.
222  int start = inputLine.indexOf('(');
223  int end = inputLine.lastIndexOf(')');
224  // Parse the args.
225  ArrayList<Integer> intList = new ArrayList<Integer>(4);
226  ArrayList<Float> floatList = new ArrayList<Float>(2);
227  ArrayList<String> stringList = new ArrayList<String>(4);
228  ArrayList<Boolean> boolList = new ArrayList<Boolean>(3);
229  parseArguments(inputLine.substring(start + 1, end),
230  intList, floatList, stringList, boolList);
231  int colon = inputLine.indexOf(':');
232  if (colon > 1 && colon < start) {
233  // This is a regular function call. Look for the name and call it.
234  String func = inputLine.substring(colon + 1, start);
235  if (func.equals("drawLine")) {
236  windows.get(windowID).drawLine(intList.get(0), intList.get(1),
237  intList.get(2), intList.get(3));
238  } else if (func.equals("createPolyline")) {
239  windows.get(windowID).createPolyline(intList.get(0));
240  } else if (func.equals("drawPolyline")) {
241  windows.get(windowID).drawPolyline();
242  } else if (func.equals("drawRectangle")) {
243  windows.get(windowID).drawRectangle(intList.get(0), intList.get(1),
244  intList.get(2), intList.get(3));
245  } else if (func.equals("setVisible")) {
246  windows.get(windowID).setVisible(boolList.get(0));
247  } else if (func.equals("setAlwaysOnTop")) {
248  windows.get(windowID).setAlwaysOnTop(boolList.get(0));
249  } else if (func.equals("addMessage")) {
250  windows.get(windowID).addMessage(stringList.get(0));
251  } else if (func.equals("addMessageBox")) {
252  windows.get(windowID).addMessageBox();
253  } else if (func.equals("clear")) {
254  windows.get(windowID).clear();
255  } else if (func.equals("setStrokeWidth")) {
256  windows.get(windowID).setStrokeWidth(floatList.get(0));
257  } else if (func.equals("drawEllipse")) {
258  windows.get(windowID).drawEllipse(intList.get(0), intList.get(1),
259  intList.get(2), intList.get(3));
260  } else if (func.equals("pen")) {
261  if (intList.size() == 4) {
262  windows.get(windowID).pen(intList.get(0), intList.get(1),
263  intList.get(2), intList.get(3));
264  } else {
265  windows.get(windowID).pen(intList.get(0), intList.get(1),
266  intList.get(2));
267  }
268  } else if (func.equals("brush")) {
269  if (intList.size() == 4) {
270  windows.get(windowID).brush(intList.get(0), intList.get(1),
271  intList.get(2), intList.get(3));
272  } else {
273  windows.get(windowID).brush(intList.get(0), intList.get(1),
274  intList.get(2));
275  }
276  } else if (func.equals("textAttributes")) {
277  windows.get(windowID).textAttributes(stringList.get(0),
278  intList.get(0),
279  boolList.get(0),
280  boolList.get(1),
281  boolList.get(2));
282  } else if (func.equals("drawText")) {
283  windows.get(windowID).drawText(intList.get(0), intList.get(1),
284  stringList.get(0));
285  } else if (func.equals("addMenuBarItem")) {
286  if (boolList.size() > 0) {
287  windows.get(windowID).addMenuBarItem(stringList.get(0),
288  stringList.get(1),
289  intList.get(0),
290  boolList.get(0));
291  } else if (intList.size() > 0) {
292  windows.get(windowID).addMenuBarItem(stringList.get(0),
293  stringList.get(1),
294  intList.get(0));
295  } else {
296  windows.get(windowID).addMenuBarItem(stringList.get(0),
297  stringList.get(1));
298  }
299  } else if (func.equals("addPopupMenuItem")) {
300  if (stringList.size() == 4) {
301  windows.get(windowID).addPopupMenuItem(stringList.get(0),
302  stringList.get(1),
303  intList.get(0),
304  stringList.get(2),
305  stringList.get(3));
306  } else {
307  windows.get(windowID).addPopupMenuItem(stringList.get(0),
308  stringList.get(1));
309  }
310  } else if (func.equals("update")) {
311  windows.get(windowID).update();
312  } else if (func.equals("showInputDialog")) {
313  windows.get(windowID).showInputDialog(stringList.get(0));
314  } else if (func.equals("showYesNoDialog")) {
315  windows.get(windowID).showYesNoDialog(stringList.get(0));
316  } else if (func.equals("zoomRectangle")) {
317  windows.get(windowID).zoomRectangle(intList.get(0), intList.get(1),
318  intList.get(2), intList.get(3));
319  } else if (func.equals("readImage")) {
320  PImage image = SVImageHandler.readImage(intList.get(2), in);
321  windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
322  } else if (func.equals("drawImage")) {
323  PImage image = new PImage(stringList.get(0));
324  windows.get(windowID).drawImage(image, intList.get(0), intList.get(1));
325  } else if (func.equals("destroy")) {
326  windows.get(windowID).destroy();
327  }
328  // else for forward compatibility purposes, silently ignore any
329  // unrecognized function call.
330  } else {
331  // No colon. Check for create window.
332  if (idStrs[1].startsWith("= luajava.newInstance")) {
333  while (windows.size() <= windowID) {
334  windows.add(null);
335  }
336  windows.set(windowID, new SVWindow(stringList.get(1),
337  intList.get(0), intList.get(1),
338  intList.get(2), intList.get(3),
339  intList.get(4), intList.get(5),
340  intList.get(6)));
341  }
342  // else for forward compatibility purposes, silently ignore any
343  // unrecognized function call.
344  }
345  } else if (inputLine.startsWith("svmain")) {
346  // Startup or end. Startup is a lua bind, which is now a no-op.
347  if (inputLine.startsWith("svmain:exit")) {
348  exit();
349  }
350  // else for forward compatibility purposes, silently ignore any
351  // unrecognized function call.
352  }
353  // else for forward compatibility purposes, silently ignore any
354  // unrecognized function call.
355  }
356 
358  public static void exit() {
359  System.exit(0);
360  }
361 
366  public static void main(String[] args) {
367  if (args.length > 0) {
368  SERVER_PORT = Integer.parseInt(args[0]);
369  }
370  windows = new ArrayList<SVWindow>(100);
371  intPattern = Pattern.compile("[0-9-][0-9]*");
372  floatPattern = Pattern.compile("[0-9-][0-9]*\\.[0-9]*");
373 
374  try {
375  // Open a socket to listen on.
376  ServerSocket serverSocket = new ServerSocket(SERVER_PORT);
377  System.out.println("Socket started on port " + SERVER_PORT);
378 
379  // Wait (blocking) for an incoming connection
380  socket = serverSocket.accept();
381  System.out.println("Client connected");
382 
383  // Setup the streams
384  out = new PrintStream(socket.getOutputStream(), true);
385  in =
386  new BufferedReader(new InputStreamReader(socket.getInputStream(),
387  "UTF8"));
388  } catch (IOException e) {
389  // Something went wrong and we were unable to set up a connection. This is
390  // pretty
391  // much a fatal error.
392  // Note: The server does not get restarted automatically if this happens.
393  e.printStackTrace();
394  System.exit(1);
395  }
396 
397  // Enter the main program loop.
398  IOLoop();
399  }
400 }
static void main(String[] args)
static void addMessage(SVEvent e)
Definition: ScrollView.java:60
static BufferedReader in
Definition: ScrollView.java:44