source: trunk/src/org/expeditee/actions/Misc.java@ 156

Last change on this file since 156 was 156, checked in by ra33, 16 years ago

Added calculate action

File size: 20.1 KB
Line 
1package org.expeditee.actions;
2
3import java.awt.Color;
4import java.awt.Image;
5import java.awt.image.BufferedImage;
6import java.awt.image.VolatileImage;
7import java.io.File;
8import java.io.FileNotFoundException;
9import java.io.IOException;
10import java.lang.reflect.Method;
11import java.text.NumberFormat;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.List;
15import java.util.Observable;
16import java.util.Observer;
17
18import javax.imageio.ImageIO;
19
20import org.expeditee.gui.AttributeUtils;
21import org.expeditee.gui.AttributeValuePair;
22import org.expeditee.gui.DisplayIO;
23import org.expeditee.gui.Frame;
24import org.expeditee.gui.FrameGraphics;
25import org.expeditee.gui.FrameIO;
26import org.expeditee.gui.FrameMouseActions;
27import org.expeditee.gui.MessageBay;
28import org.expeditee.gui.TimeKeeper;
29import org.expeditee.items.Item;
30import org.expeditee.items.Text;
31import org.expeditee.stats.CometStats;
32import org.expeditee.stats.SessionStats;
33import org.expeditee.stats.StatsLogger;
34import org.expeditee.stats.TreeStats;
35import org.nfunk.jep.Node;
36import org.nfunk.jep.Variable;
37
38/**
39 * A list of miscellaneous Actions and Actions specific to Expeditee
40 *
41 */
42public class Misc {
43 // private static final int FRAME_FILE_FONT_SIZE = 10;
44
45 /**
46 * Causes the system to beep
47 */
48 public static void Beep() {
49 java.awt.Toolkit.getDefaultToolkit().beep();
50 }
51
52 /**
53 * Forces a repaint of the current Frame
54 */
55 public static void Display() {
56 FrameGraphics.ForceRepaint();
57 }
58
59 /**
60 * Restores the current frame to the last saved version currently on the
61 * hard disk
62 */
63 public static void Restore() {
64 FrameIO.Reload();
65 // MessageBay.displayMessage("Restoration complete.");
66 }
67
68 /**
69 * Toggles AudienceMode on or off
70 */
71 public static void ToggleAudienceMode() {
72 FrameGraphics.ToggleAudienceMode();
73 }
74
75 /**
76 * Toggles TwinFrames mode on or off
77 */
78 public static void ToggleTwinFramesMode() {
79 DisplayIO.ToggleTwinFrames();
80 }
81
82 /**
83 * If the given Item is a Text Item, then the text of the Item is
84 * interpreted as actions, if not this method does nothing.
85 *
86 * @param current
87 * The Item to read the Actions from
88 */
89 public static void RunCurrentItem(Item current) {
90 if (current instanceof Text) {
91 List<String> actions = ((Text) current).getTextList();
92
93 for (String action : actions) {
94 Actions.PerformAction(DisplayIO.getCurrentFrame(), current,
95 action);
96 }
97 } else {
98 MessageBay.errorMessage("Item must be a text item.");
99 }
100 }
101
102 /**
103 * Prompts the user to confirm deletion of the current Frame, and deletes if
104 * the user chooses. After deletion this action calls back(), to ensure the
105 * deleted frame is not still being shown
106 *
107 */
108 public static void DeleteFrame() {
109 Frame toDelete = DisplayIO.getCurrentFrame();
110 String deletedFrame = toDelete.getName();
111 String deletedFrameNameLowercase = deletedFrame.toLowerCase();
112 String errorMessage = "Error deleting " + deletedFrame;
113 try {
114 String deletedFrameName = FrameIO.DeleteFrame(toDelete);
115 if (deletedFrameName != null) {
116 DisplayIO.Back();
117 // Remove any links on the previous frame to the one being
118 // deleted
119 Frame current = DisplayIO.getCurrentFrame();
120 for (Item i : current.getItems())
121 if (i.getLink() != null
122 && i.getAbsoluteLink().toLowerCase().equals(
123 deletedFrameNameLowercase)) {
124 i.setLink(null);
125 }
126 MessageBay.displayMessage(deletedFrame + " renamed "
127 + deletedFrameName);
128 // FrameGraphics.Repaint();
129 return;
130 }
131 } catch (IOException ioe) {
132 if (ioe.getMessage() != null)
133 errorMessage += ". " + ioe.getMessage();
134 } catch (SecurityException se) {
135 if (se.getMessage() != null)
136 errorMessage += ". " + se.getMessage();
137 } catch (Exception e) {
138 e.printStackTrace();
139 }
140 MessageBay.errorMessage(errorMessage);
141 }
142
143 /**
144 * Loads the Frame linked to by the given Item. The first Item on the Frame
145 * that is not the title or name is then placed on the cursor. If the given
146 * Item has no link, or no item is found then this is a no-op.
147 *
148 * @param current
149 * The Item that links to the Frame that the Item will be loaded
150 * from.
151 */
152 public static void GetItemFromChildFrame(Item current) {
153 getFromChildFrame(current, false);
154 }
155
156 /**
157 * Loads the Frame linked to by the given Item. The first Text Item on the
158 * Frame that is not the title or name is then placed on the cursor. If the
159 * given Item has no link, or no item is found then this is a no-op.
160 *
161 * @param current
162 * The Item that links to the Frame that the Item will be loaded
163 * from.
164 */
165 public static void GetTextFromChildFrame(Item current) {
166 getFromChildFrame(current, true);
167 }
168
169 private static void getFromChildFrame(Item current, boolean textOnly) {
170 Item item = getFirstBodyItemOnChildFrame(current, textOnly);
171 // if no item was found
172 if (item == null) {
173 return;
174 }
175
176 // copy the item and switch
177 item = item.copy();
178 item.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
179 item.setParent(null);
180
181 FrameMouseActions.pickup(item);
182 FrameGraphics.Repaint();
183 }
184
185 /**
186 * Sets the given Item to have the Given Color. Color can be null (for
187 * default)
188 *
189 * @param toChange
190 * The Item to set the Color.
191 * @param toUse
192 * The Color to give the Item.
193 */
194 public static void SetCurrentItemBackgroundColor(Item toChange, Color toUse) {
195 if (toChange == null)
196 return;
197
198 toChange.setBackgroundColor(toUse);
199 FrameGraphics.Repaint();
200 }
201
202 /**
203 * Sets the given Item to have the Given Color. Color can be null (for
204 * default)
205 *
206 * @param toChange
207 * The Item to set the Color.
208 * @param toUse
209 * The Color to give the Item.
210 */
211 public static void SetCurrentItemColor(Item toChange, Color toUse) {
212 if (toChange == null)
213 return;
214
215 toChange.setColor(toUse);
216 FrameGraphics.Repaint();
217 }
218
219 /**
220 * Creates a new Text Object containing general statistics for the current
221 * session. The newly created Text Object is then attached to the cursor via
222 * FrameMouseActions.pickup(Item)
223 */
224 public static void GetSessionStats() {
225 attachStatsToCursor(SessionStats.getCurrentStats());
226 }
227
228 /**
229 * Creates a new Text Object containing statistics for the current tree.
230 */
231 public static void GetCometStats(Frame frame) {
232 TimeKeeper timer = new TimeKeeper();
233 MessageBay.displayMessage("Computing comet stats...");
234
235 CometStats cometStats = new CometStats(frame);
236 attachStatsToCursor(cometStats.toString());
237 MessageBay.overwriteMessage("Comet stats time: "
238 + timer.getElapsedStringSeconds());
239 }
240
241 public static void GetTreeStats(Frame frame) {
242 TimeKeeper timer = new TimeKeeper();
243 MessageBay.displayMessage("Computing tree stats...");
244
245 TreeStats treeStats = new TreeStats(frame);
246 attachStatsToCursor(treeStats.toString());
247 MessageBay.overwriteMessage("Tree stats time: "
248 + timer.getElapsedStringSeconds());
249 }
250
251 /**
252 * Creates a text item and attaches it to the cursor.
253 *
254 * @param itemText
255 * the text to attach to the cursor
256 */
257 public static void attachStatsToCursor(String itemText) {
258 SessionStats.CreatedText();
259 Frame current = DisplayIO.getCurrentFrame();
260 Item text = current.getStatsTextItem(itemText);
261 FrameMouseActions.pickup(text);
262 FrameGraphics.Repaint();
263 }
264
265 public static void attachTextToCursor(String itemText) {
266 SessionStats.CreatedText();
267 Frame current = DisplayIO.getCurrentFrame();
268 Item text = current.getTextItem(itemText);
269 FrameMouseActions.pickup(text);
270 FrameGraphics.Repaint();
271 }
272
273 /**
274 * Creates a new Text Object containing statistics for moving, deleting and
275 * creating items in the current session. The newly created Text Object is
276 * then attached to the cursor via FrameMouseActions.pickup(Item)
277 */
278 public static void GetItemStats() {
279 attachStatsToCursor(SessionStats.getItemStats());
280 }
281
282 /**
283 * Creates a new Text Object containing statistics for the time between
284 * events triggered by the user through mouse clicks and key presses. The
285 * newly created Text Object is then attached to the cursor via
286 * FrameMouseActions.pickup(Item)
287 */
288 public static void GetEventStats() {
289 attachStatsToCursor(SessionStats.getEventStats());
290 }
291
292 /**
293 * Creates a new Text Object containing the contents of the current frames
294 * file.
295 */
296 public static void GetCurrentFrameFile() {
297 Frame current = DisplayIO.getCurrentFrame();
298 attachStatsToCursor(FrameIO.ForceSaveFrame(current));
299 }
300
301 /**
302 * Creates a new Text Object containing the available fonts.
303 */
304 public static void getFontNames() {
305
306 Collection<String> availableFonts = Actions.getFonts().values();
307 StringBuilder fontsList = new StringBuilder();
308 for (String s : availableFonts) {
309 fontsList.append(s).append(Text.LINE_SEPARATOR);
310 }
311 fontsList.deleteCharAt(fontsList.length() - 1);
312
313 attachStatsToCursor(fontsList.toString());
314 }
315
316 public static void getUnicodeCharacters(int start, int finish) {
317 if (start < 0 && finish < 0) {
318 throw new RuntimeException("Parameters must be non negative");
319 }
320 // Swap the start and finish if they are inthe wrong order
321 if (start > finish) {
322 start += finish;
323 finish = start - finish;
324 start = start - finish;
325 }
326 StringBuilder charList = new StringBuilder();
327 int count = 0;
328 charList.append(String.format("Unicode block 0x%x - 0x%x", start,
329 finish));
330 System.out.println();
331 // charList.append("Unicode block: ").append(String.format(format,
332 // args))
333 for (char i = (char) start; i < (char) finish; i++) {
334 if (Character.isDefined(i)) {
335 if (count++ % 64 == 0)
336 charList.append(Text.LINE_SEPARATOR);
337 charList.append(Character.valueOf(i));
338 }
339 }
340 attachTextToCursor(charList.toString());
341 }
342
343 /**
344 * Gets a single block of Unicode characters.
345 *
346 * @param start
347 * the start of the block
348 */
349 public static void getUnicodeCharacters(int start) {
350 getUnicodeCharacters(start, start + 256);
351 }
352
353 public static void getMathSymbols() {
354 getUnicodeCharacters('\u2200', '\u2300');
355 }
356
357 /**
358 * Resets the statistics back to zero.
359 */
360 public static void ResetStats() {
361 StatsLogger.WriteStatsFile();
362 SessionStats.resetStats();
363 }
364
365 /**
366 * Loads a frame with the given name and saves it as a JPEG image.
367 *
368 * @param framename
369 * The name of the Frame to save
370 */
371 public static void JpegFrame(String framename) {
372 ImageFrame(framename, "JPEG");
373 }
374
375 /**
376 * Saves the current frame as a JPEG image. This is the same as calling
377 * JpegFrame(currentFrame.getName())
378 */
379 public static void JpegFrame() {
380 ImageFrame(DisplayIO.getCurrentFrame().getName(), "JPEG");
381 }
382
383 public static void JPGFrame() {
384 JpegFrame();
385 }
386
387 /**
388 * Loads a frame with the given name and saves it as a PNG image.
389 *
390 * @param framename
391 * The name of the Frame to save
392 */
393 public static void PNGFrame(String framename) {
394 ImageFrame(framename, "PNG");
395 }
396
397 /**
398 * Saves the current frame as a PNG image. This is the same as calling
399 * PNGFrame(currentFrame.getName())
400 */
401 public static void PNGFrame() {
402 ImageFrame(DisplayIO.getCurrentFrame().getName(), "PNG");
403 }
404
405 public static String SaveImage(BufferedImage screen, String format,
406 String directory, String fileName) {
407 // Check if we need to append the suffix
408 if (fileName.indexOf('.') < 0)
409 fileName += "." + format.toLowerCase();
410
411 try {
412 // set up the file for output
413 String fullFileName = directory + fileName;
414 File out = new File(fullFileName);
415 if (!out.getParentFile().exists())
416 out.mkdirs();
417
418 // If the image is successfully written out return the fileName
419 if (ImageIO.write(screen, format, out))
420 return fileName;
421
422 } catch (Exception e) {
423 e.printStackTrace();
424 }
425 return null;
426 }
427
428 public static String ImageFrame(Frame frame, String format, String directory) {
429 assert (frame != null);
430
431 FrameGraphics.UpdateBuffer(frame, false);
432
433 BufferedImage screen = null;
434 Image frameBuffer = frame.getBuffer();
435 if (frame.getBuffer() instanceof BufferedImage) {
436 screen = (BufferedImage) frameBuffer;
437 } else if (frameBuffer instanceof VolatileImage) {
438 screen = ((VolatileImage) frameBuffer).getSnapshot();
439 } else {
440 assert (false);
441 }
442 return SaveImage(screen, format, directory, frame.getExportFileName());
443 }
444
445 /**
446 * Saves the Frame with the given Framename as an image of the given format.
447 *
448 * @param framename
449 * The name of the Frame to save as an image
450 * @param format
451 * The Image format to use (i.e. "PNG", "BMP", etc)
452 */
453 public static void ImageFrame(String framename, String format) {
454 Frame loaded = FrameIO.LoadFrame(framename);
455
456 // if the frame was loaded successfully
457 if (loaded != null) {
458 String path = FrameIO.IMAGES_PATH;
459 String frameName = ImageFrame(loaded, format, path);
460 if (frameName != null)
461 MessageBay.displayMessage("Frame successfully saved to " + path
462 + frameName);
463 else
464 MessageBay.errorMessage("Could not find image writer for "
465 + format + " format");
466 // if the frame was not loaded successfully, alert the user
467 } else
468 MessageBay.displayMessage("Frame '" + framename
469 + "' could not be found.");
470 }
471
472 /**
473 * Displays a message in the message box area.
474 *
475 * @param message
476 * the message to display
477 */
478 public static void MessageLn(String message) {
479 MessageBay.displayMessage(message);
480 }
481
482 public static void MessageLn2(String message, String message2) {
483 MessageBay.displayMessage(message + " " + message2);
484 }
485
486 public static void CopyFile(String existingFile, String newFileName) {
487 try {
488 // TODO is there a built in method which will do this faster?
489
490 MessageBay.displayMessage("Copying file " + existingFile + " to "
491 + newFileName + "...");
492 FrameIO.copyFile(existingFile, newFileName);
493 MessageBay.displayMessage("File copied successfully");
494 } catch (FileNotFoundException e) {
495 MessageBay.displayMessage("Error opening file: " + existingFile);
496 } catch (Exception e) {
497 MessageBay.displayMessage("File could not be copied");
498 }
499 }
500
501 /**
502 * Runs two methods alternatively a specified number of times and reports on
503 * the time spent running each method.
504 *
505 * @param fullMethodNameA
506 * @param fullMethodNameB
507 * @param repsPerTest
508 * the number of time each method is run per test
509 * @param tests
510 * the number of tests to conduct
511 *
512 */
513 public static void CompareMethods(String fullMethodNameA,
514 String fullMethodNameB, int repsPerTest, int tests) {
515 try {
516 String classNameA = getClassName(fullMethodNameA);
517 String classNameB = getClassName(fullMethodNameB);
518 String methodNameA = getMethodName(fullMethodNameA);
519 String methodNameB = getMethodName(fullMethodNameB);
520
521 Class classA = Class.forName(classNameA);
522 Class classB = Class.forName(classNameB);
523 Method methodA = classA.getDeclaredMethod(methodNameA,
524 new Class[] {});
525 Method methodB = classB.getDeclaredMethod(methodNameB,
526 new Class[] {});
527 TimeKeeper timeKeeper = new TimeKeeper();
528 long timeA = 0;
529 long timeB = 0;
530 // Run the tests
531 for (int i = 0; i < tests; i++) {
532 // Test methodA
533 timeKeeper.restart();
534 for (int j = 0; j < repsPerTest; j++) {
535 methodA.invoke((Object) null, new Object[] {});
536 }
537 timeA += timeKeeper.getElapsedMillis();
538 timeKeeper.restart();
539 // Test methodB
540 for (int j = 0; j < repsPerTest; j++) {
541 methodB.invoke((Object) null, new Object[] {});
542 }
543 timeB += timeKeeper.getElapsedMillis();
544 }
545
546 float aveTimeA = timeA * 1000F / repsPerTest / tests;
547 float aveTimeB = timeB * 1000F / repsPerTest / tests;
548 // Display Results
549 MessageBay.displayMessage("Average Execution Time");
550 MessageBay.displayMessage(methodNameA + ": "
551 + TimeKeeper.Formatter.format(aveTimeA) + "us");
552 MessageBay.displayMessage(methodNameB + ": "
553 + TimeKeeper.Formatter.format(aveTimeB) + "us");
554 } catch (Exception e) {
555 MessageBay.errorMessage(e.getClass().getSimpleName() + ": "
556 + e.getMessage());
557 }
558 }
559
560 public static String getClassName(String fullMethodName) {
561 assert (fullMethodName != null);
562 assert (fullMethodName.length() > 0);
563 int lastPeriod = fullMethodName.lastIndexOf('.');
564 if (lastPeriod > 0 && lastPeriod < fullMethodName.length() - 1)
565 return fullMethodName.substring(0, lastPeriod);
566 throw new RuntimeException("Invalid method name: " + fullMethodName);
567 }
568
569 public static String getMethodName(String methodName) {
570 assert (methodName != null);
571 assert (methodName.length() > 0);
572 int lastPeriod = methodName.lastIndexOf('.');
573 if (lastPeriod > 0 && lastPeriod < methodName.length() - 1)
574 return methodName.substring(1 + lastPeriod);
575 throw new RuntimeException("Invalid method name: " + methodName);
576 }
577
578 /**
579 * Loads the Frame linked to by the given Item. The first Item on the Frame
580 * that is not the title or name is then placed on the current frame. The
581 * item that was clicked on is placed on the frame it was linked to and the
582 * link is switched to the item from the child frame. If the given Item has
583 * no link, or no item is found then this is a no-op.
584 *
585 * @param current
586 * The Item that links to the Frame that the Item will be loaded
587 * from.
588 */
589 public static void SwapItemWithItemOnChildFrame(Item current) {
590 Item item = getFirstBodyItemOnChildFrame(current, false);
591 // if no item was found
592 if (item == null) {
593 return;
594 }
595
596 // swap the items parents
597 Frame parentFrame = current.getParent();
598 Frame childFrame = item.getParent();
599 current.setParent(childFrame);
600 item.setParent(parentFrame);
601
602 // swap the items on the frames
603 parentFrame.removeItem(current);
604 childFrame.removeItem(item);
605 parentFrame.addItem(item);
606 childFrame.addItem(current);
607
608 // swap the items links
609 item.setActions(current.getAction());
610 item.setLink(childFrame.getName());
611 current.setLink(parentFrame.getName());
612 // current.setLink(null);
613 current.setActions(null);
614
615 FrameGraphics.Repaint();
616 }
617
618 private static Item getFirstBodyItemOnChildFrame(Item current,
619 boolean textOnly) {
620 // the item must link to a frame
621 if (current.getLink() == null) {
622 MessageBay
623 .displayMessage("Cannot get item from child - this item has no link");
624 return null;
625 }
626
627 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
628
629 // if the frame could not be loaded
630 if (child == null) {
631 MessageBay.errorMessage("Could not load child frame.");
632 return null;
633 }
634
635 // find the first non-title and non-name item
636 List<Item> body = new ArrayList<Item>();
637 if (textOnly)
638 body.addAll(child.getBodyTextItems(false));
639 else
640 body.addAll(child.getItems());
641 Item item = null;
642
643 for (Item i : body)
644 if (i != child.getTitleItem() && !i.isAnnotation()) {
645 item = i;
646 break;
647 }
648
649 // if no item was found
650 if (item == null) {
651 MessageBay.displayMessage("No item found to copy");
652 return null;
653 }
654
655 return item;
656 }
657
658 public static void calculate(Frame frame, Item toCalculate) {
659 if (toCalculate instanceof Text) {
660 Text text = (Text) toCalculate;
661 org.nfunk.jep.JEP myParser = new org.nfunk.jep.JEP();
662 myParser.addStandardFunctions();
663 myParser.addStandardConstants();
664 myParser.setImplicitMul(true);
665
666 Observer observer = new Observer() {
667 private String _attribute = "";
668 public void update(Observable ob, Object o) {
669 _attribute = ((Variable)o).getName() + ": ";
670 }
671
672 @Override
673 public String toString() {
674 return _attribute;
675 }
676 };
677
678 // Check for variables
679 for (Text t : frame.getTextItems()) {
680 AttributeValuePair avp = AttributeUtils.getPair(t.getText());
681 if (avp != null) {
682 myParser.addVariable(avp.getAttribute(), avp
683 .getDoubleValue());
684 }
685 }
686
687 myParser.getSymbolTable().addObserver(observer);
688 myParser.setAllowAssignment(true);
689 myParser.setAllowUndeclared(true);
690
691 // Do the calculation
692 Node node = myParser.parseExpression(text.getText());
693 if (node == null) {
694 for (String s : myParser.getErrorInfo().split("\n")) {
695 MessageBay.errorMessage(s);
696 }
697 } else {
698 Double result = myParser.getValue();
699 NumberFormat nf = NumberFormat.getInstance();
700 nf.setMinimumFractionDigits(0);
701 nf.setMaximumFractionDigits(15);
702 text.setText(observer.toString() + nf.format(result));
703 text.setPosition(FrameMouseActions.MouseX,
704 FrameMouseActions.MouseY);
705 FrameMouseActions.resetOffset();
706 }
707
708 }
709 }
710}
Note: See TracBrowser for help on using the repository browser.