source: trunk/org/expeditee/items/ItemUtils.java@ 4

Last change on this file since 4 was 4, checked in by davidb, 16 years ago

Starting source code to Expeditee

File size: 17.9 KB
Line 
1package org.expeditee.items;
2
3import java.awt.Font;
4import java.awt.FontMetrics;
5import java.awt.Image;
6import java.awt.image.ImageObserver;
7import java.io.File;
8import java.io.UnsupportedEncodingException;
9import java.net.URL;
10import java.net.URLDecoder;
11import java.util.ArrayList;
12import java.util.LinkedList;
13import java.util.List;
14
15import javax.swing.JFrame;
16
17import org.expeditee.gui.DisplayIO;
18
19//Static methods that provide functions for the objects\
20//mostly to transform values (string -> color etc).
21
22/**
23 * Static methods that provide functions for use in Items.
24 */
25public class ItemUtils {
26 private static JFrame _jf = new JFrame();
27
28 // Tag constants
29 public static final int TAG_SORT = 0;
30
31 public static final int TAG_JOIN = 1;
32
33 public static final int TAG_INDENT = 2;
34
35 public static final int TAG_OVERLAY = 3;
36
37 public static final int TAG_ACTIVE_OVERLAY = 4;
38
39 public static final int TAG_IMAGE = 5;
40
41 public static final int TAG_ITEM_TEMPLATE = 6;
42
43 public static final int TAG_ANNOTATION_TEMPLATE = 7;
44
45 public static final int TAG_CODE_COMMENT_TEMPLATE = 8;
46
47 public static final int TAG_MENU = 9;
48
49 public static final int TAG_MENU_NEXT = 10;
50
51 public static final int TAG_PARENT = 11;
52
53 public static final int TAG_LITERAL = 12;
54
55 public static final int TAG_FRAME_IMAGE = 13;
56
57 public static final int TAG_BACKUP = 14;
58
59 public static final int TAG_POINTTYPE = 15;
60
61 public static final int TAG_MIN = 0;
62
63 public static final int TAG_MAX = 15;
64
65 /**
66 * Determines if the given List of Items contains an Item that is one of the
67 * pre-defined tags.
68 *
69 * @param items
70 * The list of Items to search through
71 * @param tag
72 * The Tag to search for, this should correspond to one of the
73 * predefined constants in this class
74 * @return True if an Item was found that is the given Tag, False otherwise.
75 */
76 public static boolean ContainsTag(List<Item> items, int tag) {
77 return ContainsTag(items, GetTag(tag));
78 }
79
80 public static boolean ContainsTag(List<Item> items, String tag) {
81 return (FindTag(items, tag) != null);
82 }
83
84 /**
85 * Searches the given List of Items for an Item that is one of the
86 * pre-defined tags.
87 *
88 * @param items
89 * The list of Items to search through
90 * @param tag
91 * The Tag to search for, this should correspond to one of the
92 * predefined constants in this class
93 * @return The Item that is the given tag if one is found, or False if none
94 * is found
95 */
96 public static Text FindTag(List<Item> items, int tag) {
97 return FindTag(items, GetTag(tag));
98 }
99
100 /**
101 * Searches the given List of Items for an Item that is the given tag
102 *
103 * @param items
104 * The list of Items to search through
105 * @param toFind
106 * The Tag to search for, this should include the at (@) symbol
107 * @return The Item that is the given tag if one is found, or False if none
108 * is found
109 */
110 public static Text FindTag(List<Item> items, String toFind) {
111 for (Item i : items) {
112 if (i instanceof Text && i.isAnnotation())
113 if (((Text) i).startsWith(toFind))
114 return (Text) i;
115 }
116
117 return null;
118 }
119
120 /**
121 * Determines if the given Item is one of the pre-defined tags in this class
122 *
123 * @param toCheck
124 * The Item to check
125 * @param tag
126 * The tag to check the Item against, this should correspond to
127 * one of the constants defined in this class
128 * @return True if the Item matches the given tag, false otherwise
129 */
130 public static boolean isTag(Item toCheck, int tag) {
131 /*
132 * if( !(toCheck instanceof Text) || !toCheck.isAnnotation()) return
133 * false;
134 *
135 * Text txt = (Text) toCheck; //tags are ase-insensitive
136 * if(String.CASE_INSENSITIVE_ORDER.compare(txt.getFirstLine(),GetTag(tag)) ==
137 * 0 || txt.startsWith(GetTag(tag) + " ")) return true;
138 *
139 * return false;
140 */
141 return isTag(toCheck, GetTag(tag));
142 }
143
144 /**
145 * Checks if the given Item contains the desired tag (case insensitive)
146 *
147 * @param toCheck
148 * The Item to check for the given tag
149 * @param tag
150 * The tag to check for in the given Item
151 * @return True if the tag is found in the given Item, False otherwise.
152 */
153 public static boolean isTag(Item toCheck, String tag) {
154 if (!(toCheck instanceof Text) || !toCheck.isAnnotation())
155 return false;
156
157 Text txt = (Text) toCheck;
158 // tags are ase-insensitive
159 if (String.CASE_INSENSITIVE_ORDER.compare(txt.getFirstLine(), tag) == 0
160 || txt.startsWith(tag + " "))
161 return true;
162
163 return false;
164 }
165
166 /**
167 * Strips off the given tag from the given String, and returns wathever is
168 * left
169 *
170 * @param toStrip
171 * The String to strip the Tag from
172 * @param tag
173 * The tag to remove from the String
174 * @return The String that results from removing the given Tag from the
175 * given String, or null if the given String is not the given Tag
176 */
177 public static String StripTag(String toStrip, String tag) {
178 if (toStrip.toLowerCase().startsWith(tag.toLowerCase())) {
179 toStrip = toStrip.substring(tag.length()).trim();
180 return toStrip;
181 }
182
183 /**
184 * TODO: Change this to use REGEX
185 */
186
187 return null;
188 }
189
190 /**
191 * The same as StripTag(String, String), but this method iterates through
192 * the list of pre-defined tags until one is found.
193 *
194 * @param toStrip
195 * The String to strip the tag from
196 * @return The String that results from stripping off the Tag, or null if
197 * the given String was not a tag
198 */
199 public static String StripTag(String toStrip) {
200 // there must be something left after stripping
201 if (toStrip == null)// || toStrip.trim().indexOf(" ") < 0)
202 return toStrip;
203
204 for (int i = TAG_MIN; i <= TAG_MAX; i++) {
205 String res = StripTag(toStrip, GetTag(i));
206 if (res != null)
207 return res;
208 }
209
210 return null;
211 }
212
213 /**
214 * Strips the first character from a string if it is the @ symbol
215 * @param toStrip
216 * the string to be stripped
217 * @return the stripped version of the string
218 */
219 public static String StripTagSymbol(String toStrip) {
220 // there must be something left after stripping
221 if (toStrip != null) {
222 if (toStrip.length() > 0) {
223 if (toStrip.charAt(0) == '@') {
224 return toStrip.substring(1);
225 }
226 }
227 }
228
229 return toStrip;
230 }
231
232 /**
233 * Converts the given int to the String tag. The int should correspond to
234 * one of the constants in this class, if it does not this method will
235 * return null.
236 *
237 * @param tag
238 * The int corresponding to the constants in this class of which
239 * tag to return
240 * @return The String representation of the given Tag, or null if the given
241 * value does not have a tag associated
242 */
243 public static String GetTag(int tag) {
244 switch (tag) {
245 case TAG_SORT:
246 return "@sort";
247 case TAG_JOIN:
248 return "@join";
249 case TAG_INDENT:
250 return "@indent";
251 case TAG_OVERLAY:
252 return "@o";
253 case TAG_ACTIVE_OVERLAY:
254 return "@ao";
255 case TAG_IMAGE:
256 return "@i:";
257 case TAG_ITEM_TEMPLATE:
258 return "@itemtemplate";
259 case TAG_ANNOTATION_TEMPLATE:
260 return "@annotationtemplate";
261 case TAG_CODE_COMMENT_TEMPLATE:
262 return "@commenttemplate";
263 case TAG_MENU:
264 return "@menu";
265 case TAG_MENU_NEXT:
266 return "@nextmenu";
267 case TAG_PARENT:
268 return "@parent";
269 case TAG_LITERAL:
270 return "@lit";
271 case TAG_FRAME_IMAGE:
272 return "@f";
273 case TAG_BACKUP:
274 return "@old";
275 case TAG_POINTTYPE:
276 return "@pointtype:";
277 default:
278 return null;
279 }
280 }
281
282 /**
283 * Returns a FontMetrics object that corresponds to the given Font.
284 *
285 * @param font
286 * The Font to derive the FontMetrics from
287 * @return FontMetrics that correspond to the given Font
288 */
289 public static FontMetrics getFontMetrics(Font font) {
290 return _jf.getFontMetrics(font);
291 }
292
293 public static Image CreateImage(int x, int y) {
294 return _jf.createImage(x, y);
295 }
296
297 /**
298 * Creates a picture object from the information stored in the given Text
299 * object. <br>
300 * The paths searched are in the following order:<br>
301 * /images/<br>
302 * the source text as a relative path (from program root folder). <br>
303 * the source text as an absolute path <br>
304 * <br>
305 * If the Image file cannot be found on disk null is returned.
306 *
307 * @param source
308 * The Text file containing the Picture infomation
309 * @return The Picture object representing the file, or Null if the file is
310 * not found.
311 */
312 public static Picture CreatePicture(Text source, ImageObserver observer) {
313 String text = source.getTextNoList();
314 String path = "";
315 String size = "";
316
317 try {
318 // remove @i tag
319 text = text.replaceFirst("@i:", "");
320 text = text.replaceAll("\n", "");
321 text = text.trim();
322
323 if (text.indexOf(".") >= text.lastIndexOf(" "))
324 path = text;
325 else
326 path = text.substring(0, text.lastIndexOf(" "));
327
328 size = text.substring(path.length()).trim();
329
330 // try images subdirectory
331 File file = null;
332
333 for (String dir : org.expeditee.gui.UserSettings.ImageDirs) {
334 file = new File(dir + path);
335 if (file.exists() && !file.isDirectory())
336 break;
337 }
338
339 if (file == null || !file.exists() || file.isDirectory())
340 file = new File(path);
341
342 // try relative path
343 if (!file.exists() || file.isDirectory()) {
344 URL picture = new Object().getClass().getResource(path);
345
346 // decode to remove %20 in windows folder names
347 if (picture != null) {
348 try {
349 path = URLDecoder.decode(picture.getFile(), "UTF-8");
350 } catch (UnsupportedEncodingException e) {
351 // TODO Auto-generated catch block
352 e.printStackTrace();
353 }
354 }
355
356 } else
357 path = file.getPath();
358
359 // if the image isn't found by now, give up.
360 file = new File(path);
361 if (!file.exists() || file.isDirectory()) {
362 return null;
363 }
364
365 } catch (Exception e) {
366 return null;
367 }
368
369 try {
370 Picture pic = new Picture(source, path, size, observer);
371
372 return pic;
373 } catch (Exception e) {
374 e.printStackTrace();
375 return null;
376 }
377
378 }
379
380 public static Picture CreateFramePicture(Text source, ImageObserver observer) {
381 String size = source.getFirstLine();
382
383 // remove @f tag
384 size = size.replaceFirst("@f", "");
385 size = size.trim();
386
387 try {
388 Picture pic = new Picture(source, null, size, observer);
389 return pic;
390 } catch (Exception e) {
391 //e.printStackTrace();
392 return null;
393 }
394 }
395
396 /**
397 * Creates a deep copy of the given List of Items.
398 *
399 * @param toCopy
400 * The list of Items to copy
401 * @return A list containing a copy of all Items in the given List
402 */
403 public static List<Item> CopyItems(List<Item> toCopy) {
404 // The copies to return
405 List<Item> copies = new ArrayList<Item>();
406
407 // list of dots at the end of lines
408 List<Item> lineEnds = new ArrayList<Item>();
409 List<Line> lines = new ArrayList<Line>();
410
411 List<Item> vConst = new ArrayList<Item>();
412 List<Item> hConst = new ArrayList<Item>();
413
414 List<Item> seen = new ArrayList<Item>();
415
416 for (Item i : toCopy) {
417 // lines are recreated later
418 if (i instanceof Line) {
419 Line line = (Line) i;
420 lines.add(line);
421
422 lineEnds.add(line.getEndItem());
423 lineEnds.add(line.getStartItem());
424
425 List<Constraint> consts = line.getStartItem().getConstraints();
426 if (consts != null) {
427 for (Constraint c : consts) {
428 if (c.contains(line.getEndItem())) {
429 if (c.getConstraintType() == Constraint.VERTICAL) {
430 vConst.add(line.getStartItem());
431 vConst.add(line.getEndItem());
432 } else {
433 hConst.add(line.getStartItem());
434 hConst.add(line.getEndItem());
435 }
436 }
437 }
438 }
439 } else {
440 seen.add(i);
441 }
442 }
443
444 // only dots that are not line ends are copied
445 seen.removeAll(lineEnds);
446
447 // copy all single dots
448 for (Item i : seen) {
449 Item copy = i.copy();
450
451 if (i.getParent() != null) {
452 // if this is the frame name, make sure the frame is saved (in
453 // case it is a TDFC frame)
454 if (i == i.getParent().getName())
455 i.getParent().setChanged(true);
456
457 // if this is the title of the frame, link it to the frame
458 if (i.getLink() == null && i == i.getParent().getTitle()
459 && toCopy.size() == 1) {
460 // save the frame after copying
461 i.getParent().setChanged(true);
462 copy.setLink(i.getParent().getFrameName());
463 }
464 }
465
466 copies.add(copy);
467 }
468
469 seen.clear();
470
471 // replace line ends with their copies
472 // this is done here so that copied lines can still share end points
473 for (Item i : lineEnds) {
474 // if a copy of this dot has not yet been created
475 // only copy dots that are in the list of dots to copy, leave
476 // others
477 // untouched
478 if (!seen.contains(i) && toCopy.contains(i)) {
479 // create a copy of the line end
480 Item copy = i.copy();
481
482 //copy.setID(DisplayIO.getCurrentFrame().getNextItemID());
483
484 copy.removeAllLines();
485 copy.removeAllConstraints();
486
487 // replace all other instances with the copy
488 ReplaceAll(lineEnds, i, copy);
489 ReplaceAll(vConst, i, copy);
490 ReplaceAll(hConst, i, copy);
491
492 seen.add(i);
493 copies.add(copy);
494 }
495 }
496
497 // recreate lines
498 for (int i = 0; i < lineEnds.size(); i += 2) {
499 Line line = (Line) lines.get(i / 2).copy();
500
501 line.setID(DisplayIO.getCurrentFrame().getNextItemID());
502 line.setStartItem(lineEnds.get(i));
503 line.setEndItem(lineEnds.get(i + 1));
504
505 copies.add(line);
506 }
507
508 // recreate vertical constraints
509 for (int i = 0; i < vConst.size(); i += 2) {
510 new Constraint(vConst.get(i), vConst.get(i + 1), DisplayIO
511 .getCurrentFrame().getNextItemID(), Constraint.VERTICAL);
512 }
513
514 // recreate horizontal constraints
515 for (int i = 0; i < hConst.size(); i += 2) {
516 new Constraint(hConst.get(i), hConst.get(i + 1), DisplayIO
517 .getCurrentFrame().getNextItemID(), Constraint.HORIZONTAL);
518 }
519
520 return copies;
521 }
522
523 private static void ReplaceAll(List<Item> list, Item replace, Item with) {
524 // replace all other instances with the copy
525 for (int pos = 0; pos < list.size(); pos++)
526 if (list.get(pos) == replace) {
527 list.set(pos, with);
528 }
529 }
530
531 /**
532 * Attempts to create a new line that starts from the given Item
533 * ('unreeling'). The Item must already have at least one line, and not be a
534 * line itself to be unreeled from.
535 *
536 * @param toUnreelFrom
537 * The Item that will be one end point of the new line
538 * @return A List containing the newly created Item and Line that unreel
539 * from the given Item, or null if this Item cannot be unreeled
540 * from.
541 */
542 public static List<Item> UnreelLine(Item toUnreelFrom) {
543 // the Item must already have one line to be unreeled from
544 if (toUnreelFrom == null || toUnreelFrom.getLines().size() < 1)
545 return null;
546
547 List<Item> unreel = new ArrayList<Item>(2);
548 unreel.add(toUnreelFrom);
549 unreel.addAll(toUnreelFrom.getLines());
550 return UnreelLine(unreel);
551 }
552
553 /**
554 * Attempts to create a new line that starts from the given list of Items
555 * ('unreeling'). The List must contain only one non-line Item. The non-line
556 * Item must already have at least one line to be unreeled from.
557 *
558 * @param toUnreel
559 * The List containing the Item that will be one end point of the
560 * new line
561 * @return A List of the newly created Item and Line that unreel from the
562 * Item in the given List, or null if this List cannot be unreeled
563 * from.
564 */
565 public static List<Item> UnreelLine(List<Item> toUnreel) {
566 Item origEnd = null;
567 // find the end being unreeled from
568 for (Item item : toUnreel) {
569 // we dont want to unreel anything other than lines
570 if (!(item instanceof Dot || item instanceof Line)) {
571 return null;
572 }
573 //find the dot to unreel from
574 if (item instanceof Dot) {
575 // if there are multiple ends in the list, return
576 if (origEnd != null)
577 return null;
578
579 origEnd = item;
580 }
581 }
582
583 // copy the original endpoint
584 Item copy = origEnd.copy();
585 copy.removeAllLines();
586 copy.removeAllConstraints();
587
588 int num = 0;
589
590 //Mike: WHAT IS THIS CODE DOING?!!?
591 for (Line line : origEnd.getLines())
592 num = Math.min(num, line.getID());
593
594 num--;
595
596 // create a new line
597 Line line = new Line(origEnd, copy, num);
598 //copy.setFloating(true);
599 origEnd.setArrowheadLength(0);
600 copy.setArrowheadLength(0);
601
602 List<Item> toReturn = new LinkedList<Item>();
603 toReturn.add(copy);
604 toReturn.add(line);
605 return toReturn;
606 }
607
608 /**
609 * Checks through all Lines and Dots on the current Frame to detect if any
610 * form an enclosure, which can then be used to manipulate items within the
611 * polygon. If an enclosure is found, then the dots will have their
612 * enclosure value set to true, and a List is created that contains all the
613 * Dots in the order they were processed. Actual calculation of the Polygon
614 * is done dynamically (to account for Dots being moved).
615 */
616 public static void EnclosedCheck(List<Item> items) {
617 _seen.clear();
618
619 // get all dots on the Frame
620 List<Dot> dots = new ArrayList<Dot>(0);
621 for (Item i : items) {
622 if (i instanceof Dot) {
623 Dot d = (Dot) i;
624 d.setEnclosedList(null);
625
626 if (d.getLines().size() == 2)
627 dots.add(d);
628 }
629 }
630
631 // if there are no dots on the Frame, then there can't be an enclosure
632 if (dots.size() == 0)
633 return;
634
635 // iterate through all the dots
636 for (Dot searchFor : dots) {
637 _seen.clear();
638
639 for (Line l : searchFor.getLines()) {
640 _seen.add(l);
641 if (traverse(searchFor, l.getOppositeEnd(searchFor))) {
642 _path.add((Dot) l.getOppositeEnd(searchFor));
643
644 for (Dot d : _path)
645 d.setEnclosedList(_path);
646
647 _path = new ArrayList<Dot>(0);
648
649 break;
650 }
651 }
652 }
653
654 }
655
656 private static List<Line> _seen = new ArrayList<Line>();
657
658 private static List<Dot> _path = new ArrayList<Dot>();
659
660 private static boolean traverse(Dot toFind, Item searchFrom) {
661 if (toFind == null || searchFrom == null
662 || !(searchFrom instanceof Dot))
663 return false;
664
665 if (searchFrom.getLines().size() != 2)
666 return false;
667
668 if (toFind == searchFrom)
669 return true;
670
671 for (Line l : searchFrom.getLines()) {
672 if (!(_seen.contains(l))) {
673 _seen.add(l);
674 if (traverse(toFind, l.getOppositeEnd(searchFrom))) {
675 _path.add((Dot) l.getOppositeEnd(searchFrom));
676 return true;
677 }
678 }
679
680 }
681
682 return false;
683 }
684}
Note: See TracBrowser for help on using the repository browser.