Changeset 975 for trunk/src/org/expeditee
- Timestamp:
- 11/24/15 11:17:31 (9 years ago)
- Location:
- trunk/src/org/expeditee
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/expeditee/actions/Actions.java
r919 r975 20 20 21 21 import java.awt.GraphicsEnvironment; 22 import java.io.File;23 import java.io.IOException;24 22 import java.lang.reflect.Constructor; 25 23 import java.lang.reflect.Method; 26 24 import java.lang.reflect.Modifier; 27 import java.net.URL;28 import java.net.URI;29 import java.net.URLDecoder;30 import java.net.JarURLConnection;31 25 import java.rmi.UnexpectedException; 32 import java.util.ArrayList;33 26 import java.util.Collection; 34 import java.util. Enumeration;27 import java.util.Comparator; 35 28 import java.util.HashMap; 36 29 import java.util.LinkedList; 37 30 import java.util.List; 38 31 import java.util.Set; 39 import java.util.jar.JarEntry;40 import java.util.jar.JarFile;41 import java.util.zip.ZipEntry;42 32 43 33 import org.expeditee.agents.Agent; … … 54 44 import org.expeditee.items.Text; 55 45 import org.expeditee.reflection.PackageLoader; 56 import org.expeditee.settings.UserSettings;57 46 import org.expeditee.simple.SString; 58 47 import org.expeditee.stats.Logger; … … 61 50 * The Action class is used to launch Actions and Agents. 62 51 * 63 * This class checks all class files in the same directory, and reads in and adds all the methods from them. The methods 64 * are stored in a Hashtable so that the lowercase method names can be mapped to the correctly capatilized method names 65 * (to provide case-insensitivity) 52 * This class checks all class files in the same directory, and reads in and 53 * adds all the methods from them. The methods are stored in a Hashtable so that 54 * the lowercase method names can be mapped to the correctly capatilized method 55 * names (to provide case-insensitivity) 66 56 * 67 * When adding an action to a class in the actions folder the following must be considered: <li>If the first parameter 68 * is of type Frame, the current frame will be passed as a parameter. <li>If the next param is of type Item the item on 69 * the end of the cursor will be passed or the item that was clicked to execute the action if nothing is on the end of 70 * the cursor. current frame or item.</li> <li>If there are multiple overloads for the same method they should be 71 * declared in order of the methods with the most parameteres to least parameters.</li> 57 * When adding an action to a class in the actions folder the following must be 58 * considered: <li>If the first parameter is of type Frame, the current frame 59 * will be passed as a parameter. <li>If the next param is of type Item the item 60 * on the end of the cursor will be passed or the item that was clicked to 61 * execute the action if nothing is on the end of the cursor. current frame or 62 * item.</li> <li>If there are multiple overloads for the same method they 63 * should be declared in order of the methods with the most parameteres to least 64 * parameters.</li> 72 65 */ 73 66 public class Actions { … … 99 92 public static final String WIDGET_PACKAGE = ROOT_PACKAGE + "items.widgets."; 100 93 101 public static final String CHARTS_PACKAGE = ROOT_PACKAGE + "items.widgets.charts."; 102 103 public static final String NAVIGATIONS_CLASS = ROOT_PACKAGE + "actions.NavigationActions"; 94 public static final String CHARTS_PACKAGE = ROOT_PACKAGE 95 + "items.widgets.charts."; 96 97 public static final String NAVIGATIONS_CLASS = ROOT_PACKAGE 98 + "actions.NavigationActions"; 104 99 105 100 // public static Class[] getClasses(String pckgname) … … 213 208 214 209 /** 215 * Clears out the Action and JAG Hashtables and refills them. Normally this is only called once when the system 216 * starts. 217 * 218 * @return a warning message if there were any problems loading agents or actions. 210 * Clears out the Action and JAG Hashtables and refills them. Normally this 211 * is only called once when the system starts. 212 * 213 * @return a warning message if there were any problems loading agents or 214 * actions. 219 215 */ 220 216 public static Collection<String> Init() { … … 232 228 } 233 229 234 235 230 classes = PackageLoader.getClassesNew(WIDGET_PACKAGE); 236 231 … … 241 236 } 242 237 243 244 238 classes = PackageLoader.getClassesNew(CHARTS_PACKAGE); 245 239 … … 282 276 283 277 /** 284 * Temporary, if a plugin system is devised then this would porbably become redundant. For now this allows external285 * agents to be included.278 * Temporary, if a plugin system is devised then this would porbably become 279 * redundant. For now this allows external agents to be included. 286 280 * 287 281 * @param fullClassNames 288 * A set of full class names, that is, the class package and name. For 289 * example" "org.myplugin.agents.SerializedSearch" 290 * 291 * @return A collection of classes their were omitted because either there was a name clash with existing agents or 292 * did not exist. i.e. is completely successful this will be empty. Never null. 282 * A set of full class names, that is, the class package and 283 * name. For example" "org.myplugin.agents.SerializedSearch" 284 * 285 * @return A collection of classes their were omitted because either there 286 * was a name clash with existing agents or did not exist. i.e. is 287 * completely successful this will be empty. Never null. 293 288 * 294 289 * @throws NullPointerException … … 335 330 336 331 /** 337 * Loads all the Methods that meet the requirements checked by MethodCheck into the hashtable. 332 * Loads all the Methods that meet the requirements checked by MethodCheck 333 * into the hashtable. 338 334 * 339 335 * @param c … … 365 361 366 362 /** 367 * Checks if the given Method corresponds to the restrictions of Action commands, namely: Declared (not inherited), 368 * Public, and Static, with a void return type. 363 * Checks if the given Method corresponds to the restrictions of Action 364 * commands, namely: Declared (not inherited), Public, and Static, with a 365 * void return type. 369 366 * 370 367 * @param m … … 392 389 393 390 /** 394 * Performs the given action command. The source Frame and Item are given because they are required by some actions. 395 * Note that the source frame does not have to be the Item's parent Frame. 391 * Performs the given action command. The source frame and item are given 392 * because they are required by some actions. Note that the source frame 393 * does not ahve to be the items parent frame. 394 * 395 * If multiple actions exist with the name command.takeWhile(_ != ' ') then 396 * it attempts to find candidate that best matches the arguments passed in. 397 * (arguments are command.dropWhile(_ != ' ') or is floating) 396 398 * 397 399 * @param source 398 * The Frame that the action should apply to400 * The frame that the action should apply to 399 401 * @param launcher 400 * The Item that has the action assigned to it402 * The item that has the action assigned to it 401 403 * @param command 402 404 * The action to perform 403 */ 404 public static Object PerformAction(Frame source, Item launcher, String command) throws Exception { 405 // if (!command.equalsIgnoreCase("Restore")) 406 // FrameIO.SaveFrame(source, false); 407 // TODO make restore UNDO the changes made by the last action 408 409 // separate method name and parameter names 410 String mname = getName(command); 411 command = command.substring(mname.length()).trim(); 412 // If no params are provided get them from a text item on the cursor 413 if (command.length() == 0 && launcher instanceof Text && launcher.isFloating()) { 414 command = launcher.getText(); 415 } 416 417 // Strip off the @ from annotation items 418 if (mname.startsWith("@")) 419 mname = mname.substring(1); 420 421 mname = mname.trim(); 422 String lowercaseName = mname.toLowerCase(); 423 // check for protection on frame 424 if (ItemUtils.ContainsTag(source.getItems(), "@No" + mname)) { 425 throw new RuntimeException("Frame is protected by @No" + mname + " tag."); 426 } 427 428 // retrieve methods that match the name 429 Method toRun = _Actions.get(lowercaseName); 430 431 // if this is not the name of a method, it may be the name of an agent 432 if (toRun == null) { 433 LaunchAgent(mname, command, source, launcher); 405 * @return 406 * @throws Exception 407 */ 408 public static Object PerformAction(final Frame source, final Item launcher, 409 final String command) throws Exception { 410 System.err.println("Running action: " + command + " with floating: " + launcher); 411 final String actionName = getName(command); 412 final String parameters = command.substring(actionName.length()).trim(); 413 414 // Check for protection on frame. 415 if (ItemUtils.ContainsTag(source.getItems(), "@No" + actionName)) { 416 final String errorMsg = "Frame is protected by @No" + actionName 417 + " tag."; 418 MessageBay.errorMessage(errorMsg); 419 throw new RuntimeException(errorMsg); 420 } 421 422 // Find all canditates. Sort by quantity of arguments. 423 final String lowercaseName = actionName.toLowerCase(); 424 final Method firstCanditate = _Actions.get(lowercaseName); 425 // if this is not the name of a method, it may be the name of an agent 426 if (firstCanditate == null) { 427 LaunchAgent(actionName, command, source, launcher); 434 428 return null; 435 429 } 436 437 // Need to save the frame if we are navigating away from it so we dont 438 // loose changes 439 if (toRun.getDeclaringClass().getName().equals(NAVIGATIONS_CLASS)) { 430 // need to save the frame if we are navigating away from it so we dont 431 // loose changes 432 if (firstCanditate.getDeclaringClass().getName().equals(NAVIGATIONS_CLASS)) { 440 433 FrameIO.SaveFrame(DisplayIO.getCurrentFrame()); 441 434 } 442 443 // if there are duplicate methods with the same name 444 List<Method> possibles = new LinkedList<Method>(); 445 possibles.add(toRun); 435 final List<Method> canditates = new LinkedList<Method>(); 436 canditates.add(firstCanditate); 446 437 int i = 0; 447 while (_Actions.containsKey(lowercaseName + i)) { 448 possibles.add(_Actions.get(lowercaseName + i)); 449 i++; 450 } 451 452 for (Method possible : possibles) { 453 // try first with the launching item as a parameter 454 455 // run method 438 while (_Actions.containsKey(lowercaseName + i)) 439 canditates.add(_Actions.get(lowercaseName + i++)); 440 canditates.sort(new Comparator<Method>() { 441 @Override 442 public int compare(final Method m1, final Method m2) { 443 return m2.getParameterCount() - m1.getParameterCount(); 444 } 445 }); 446 447 // Find best candidate. Start searching with the candidate with most 448 // arguments. 449 for (final Method canditate : canditates) { 450 final Object[] paramObjects = CreateObjects(canditate, source, 451 launcher, parameters); 456 452 try { 457 // convert parameters to objects and get the method to invoke 458 Object[] parameters = CreateObjects(possible, source, launcher, command); 459 // Check that there are the same amount of params 460 if (parameters == null) { 461 continue; 462 } 463 464 return possible.invoke(null, parameters); 465 } catch (Exception e) { 453 if (paramObjects != null) 454 return canditate.invoke(null, paramObjects); 455 } catch (final Exception e) { 466 456 Logger.Log(e); 467 457 e.printStackTrace(); 468 458 } 469 459 } 470 // If the actions was not found... then it is run as an agent 471 assert (possibles.size() > 0); 472 throw new RuntimeException("Incorrect parameters for " + mname); 473 } 460 assert (canditates.size() > 0); 461 final String nl = System.getProperty("line.separator"); 462 final StringBuilder errorBuilder = new StringBuilder( 463 "Incorrect parameters for " + actionName + nl + "Canditates: "); 464 for (final Method canditate : canditates) 465 errorBuilder.append(canditate + nl); 466 throw new RuntimeException(errorBuilder.toString()); 467 } 468 469 // /** 470 // * Performs the given action command. The source Frame and Item are given 471 // because they are required by some actions. 472 // * Note that the source frame does not have to be the Item's parent Frame. 473 // * 474 // * @param source 475 // * The Frame that the action should apply to 476 // * @param launcher 477 // * The Item that has the action assigned to it 478 // * @param command 479 // * The action to perform 480 // */ 481 // public static Object PerformAction(Frame source, Item launcher, String 482 // command) throws Exception { 483 // // if (!command.equalsIgnoreCase("Restore")) 484 // // FrameIO.SaveFrame(source, false); 485 // // TODO make restore UNDO the changes made by the last action 486 // 487 // // separate method name and parameter names 488 // String mname = getName(command); 489 // command = command.substring(mname.length()).trim(); 490 // // If no params are provided get them from a text item on the cursor 491 // if (command.length() == 0 && launcher instanceof Text && 492 // launcher.isFloating()) { 493 // command = launcher.getText(); 494 // } 495 // 496 // // Strip off the @ from annotation items 497 // if (mname.startsWith("@")) 498 // mname = mname.substring(1); 499 // 500 // mname = mname.trim(); 501 // String lowercaseName = mname.toLowerCase(); 502 // // check for protection on frame 503 // if (ItemUtils.ContainsTag(source.getItems(), "@No" + mname)) { 504 // throw new RuntimeException("Frame is protected by @No" + mname + 505 // " tag."); 506 // } 507 // 508 // // retrieve methods that match the name 509 // Method toRun = _Actions.get(lowercaseName); 510 // 511 // // if this is not the name of a method, it may be the name of an agent 512 // if (toRun == null) { 513 // LaunchAgent(mname, command, source, launcher); 514 // return null; 515 // } 516 // 517 // // Need to save the frame if we are navigating away from it so we dont 518 // // loose changes 519 // if (toRun.getDeclaringClass().getName().equals(NAVIGATIONS_CLASS)) { 520 // FrameIO.SaveFrame(DisplayIO.getCurrentFrame()); 521 // } 522 // 523 // // if there are duplicate methods with the same name 524 // List<Method> possibles = new LinkedList<Method>(); 525 // possibles.add(toRun); 526 // int i = 0; 527 // while (_Actions.containsKey(lowercaseName + i)) { 528 // possibles.add(_Actions.get(lowercaseName + i)); 529 // i++; 530 // } 531 // 532 // for (Method possible : possibles) { 533 // // try first with the launching item as a parameter 534 // 535 // // run method 536 // try { 537 // // convert parameters to objects and get the method to invoke 538 // Object[] parameters = CreateObjects(possible, source, launcher, command); 539 // // Check that there are the same amount of params 540 // if (parameters == null) { 541 // continue; 542 // } 543 // 544 // return possible.invoke(null, parameters); 545 // } catch (Exception e) { 546 // Logger.Log(e); 547 // e.printStackTrace(); 548 // } 549 // } 550 // // If the actions was not found... then it is run as an agent 551 // assert (possibles.size() > 0); 552 // throw new RuntimeException("Incorrect parameters for " + mname); 553 // } 474 554 475 555 /** … … 483 563 * The starting Frame that the JAG is being launched on 484 564 */ 485 private static void LaunchAgent(String name, String parameters, Frame source, Item clicked) throws Exception { 565 private static void LaunchAgent(String name, String parameters, 566 Frame source, Item clicked) throws Exception { 486 567 // Use the correct case version for printing error messages 487 568 String nameWithCorrectCase = name; … … 495 576 fullClassName = _JAGs.get(name); 496 577 } else if (name.endsWith("tree")) { 497 parameters = name.substring(0, name.length() - "tree".length()) + " " + parameters; 578 parameters = name.substring(0, name.length() - "tree".length()) 579 + " " + parameters; 498 580 fullClassName = AGENTS_PACKAGE + "writetree"; 499 581 500 582 } else if (name.endsWith("frame")) { 501 parameters = name.substring(0, name.length() - "frame".length()) + " " + parameters; 583 parameters = name 584 .substring(0, name.length() - "frame".length()) 585 + " " 586 + parameters; 502 587 fullClassName = AGENTS_PACKAGE + "writeframe"; 503 588 } … … 520 605 String[] paramStrings = parameters.split("\\s+"); 521 606 /** 522 * Any extra parameters will be treated as the rest of the string if the last param is a string 607 * Any extra parameters will be treated as the rest of the 608 * string if the last param is a string 523 609 */ 524 610 if (paramCount > paramStrings.length) { … … 527 613 528 614 /** 529 * If there are extra parameters the last param must be a String 615 * If there are extra parameters the last param must be a 616 * String 530 617 */ 531 618 int lastParam = paramTypes.length - 1; 532 619 533 if (paramCount < paramStrings.length && !paramTypes[lastParam].equals(String.class)) { 620 if (paramCount < paramStrings.length 621 && !paramTypes[lastParam].equals(String.class)) { 534 622 continue; 535 623 } … … 539 627 SString nextParam = new SString(paramStrings[i]); 540 628 params[i] = null; 541 if (paramTypes[i].equals(int.class) || paramTypes[i].equals(Integer.class)) { 629 if (paramTypes[i].equals(int.class) 630 || paramTypes[i].equals(Integer.class)) { 542 631 params[i] = nextParam.integerValue().intValue(); 543 } else if (paramTypes[i].equals(long.class) || paramTypes[i].equals(Long.class)) { 632 } else if (paramTypes[i].equals(long.class) 633 || paramTypes[i].equals(Long.class)) { 544 634 params[i] = nextParam.integerValue(); 545 } else if (paramTypes[i].equals(double.class) || paramTypes[i].equals(Double.class)) { 635 } else if (paramTypes[i].equals(double.class) 636 || paramTypes[i].equals(Double.class)) { 546 637 params[i] = nextParam.doubleValue(); 547 } else if (paramTypes[i].equals(float.class) || paramTypes[i].equals(Float.class)) { 548 params[i] = nextParam.doubleValue().floatValue(); 549 } else if (paramTypes[i].equals(boolean.class) || paramTypes[i].equals(Boolean.class)) { 638 } else if (paramTypes[i].equals(float.class) 639 || paramTypes[i].equals(Float.class)) { 640 params[i] = nextParam.doubleValue() 641 .floatValue(); 642 } else if (paramTypes[i].equals(boolean.class) 643 || paramTypes[i].equals(Boolean.class)) { 550 644 params[i] = nextParam.booleanValue(); 551 645 } else if (paramTypes[i].equals(String.class)) { 552 646 params[i] = nextParam.stringValue(); 553 647 } else { 554 throw new UnexpectedException("Unexpected type " + paramTypes[i].getClass().toString()); 648 throw new UnexpectedException( 649 "Unexpected type " 650 + paramTypes[i].getClass() 651 .toString()); 555 652 } 556 653 } … … 562 659 563 660 /** 564 * Append extra params on the end of the last string param 661 * Append extra params on the end of the last string 662 * param 565 663 */ 566 664 String s = params[lastParam].toString(); … … 581 679 // if there is no constructor, return 582 680 if (con == null) { 583 throw new RuntimeException(INVALID_PARAMETERS_ERROR + nameWithCorrectCase); 681 throw new RuntimeException(INVALID_PARAMETERS_ERROR 682 + nameWithCorrectCase); 584 683 } 585 684 … … 591 690 } catch (ClassNotFoundException cnf) { 592 691 _Agent = null; 593 throw new RuntimeException("'" + nameWithCorrectCase + "' is not an action or agent."); 594 } 595 } 596 597 public static void LaunchAgent(String name, String parameters, Frame source) throws Exception { 692 throw new RuntimeException("'" + nameWithCorrectCase 693 + "' is not an action or agent."); 694 } 695 } 696 697 public static void LaunchAgent(String name, String parameters, Frame source) 698 throws Exception { 598 699 LaunchAgent(name, parameters, source, null); 599 700 } … … 630 731 631 732 Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() { 632 public void uncaughtException(Thread th, Throwable ex) { 633 634 MessageBay.errorMessage("Error occurred in Action: " + th.getName()); 635 ex.printStackTrace(); 636 637 stopAgent(); 638 _Agent = null; 639 } 733 public void uncaughtException(Thread th, Throwable ex) { 734 735 MessageBay.errorMessage("Error occurred in Action: " 736 + th.getName()); 737 ex.printStackTrace(); 738 739 stopAgent(); 740 _Agent = null; 741 } 640 742 }; 641 642 Thread t = new Thread(_Agent, nameWithCorrectCase);643 743 744 Thread t = new Thread(_Agent, nameWithCorrectCase); 745 644 746 t.setPriority(Thread.MIN_PRIORITY); 645 747 t.setUncaughtExceptionHandler(h); 646 748 647 749 if (FreeItems.textOnlyAttachedToCursor()) { 648 750 itemParam = FreeItems.getItemAttachedToCursor(); … … 652 754 if (!_Agent.initialise(source, itemParam)) { 653 755 _Agent = null; 654 throw new RuntimeException("Error initialising agent: " + nameWithCorrectCase); 756 throw new RuntimeException("Error initialising agent: " 757 + nameWithCorrectCase); 655 758 } 656 759 … … 658 761 // TODO make this nicer... ie. make Format an action rather than an 659 762 // agent and save frames only before running agents 660 if (!nameWithCorrectCase.equalsIgnoreCase("format") && !nameWithCorrectCase.equalsIgnoreCase("sort")) { 763 if (!nameWithCorrectCase.equalsIgnoreCase("format") 764 && !nameWithCorrectCase.equalsIgnoreCase("sort")) { 661 765 FrameUtils.LeavingFrame(source); 662 766 } … … 667 771 t.run(); 668 772 if (_Agent != null) { 669 String result = _Agent.toString(); 670 // Attach the result to the cursor 671 if (FreeItems.textOnlyAttachedToCursor()) { 672 Item resultItem = FreeItems.getItemAttachedToCursor(); 673 resultItem.setText(result); 674 } 675 // if there is a completion frame, then display it to the user 773 String result = _Agent.toString(); 774 // Attach the result to the cursor 775 if (FreeItems.textOnlyAttachedToCursor()) { 776 Item resultItem = FreeItems.getItemAttachedToCursor(); 777 resultItem.setText(result); 778 } 779 // if there is a completion frame, then display it to the 780 // user 676 781 } 677 782 } else { … … 687 792 _Agent = null; 688 793 e.printStackTrace(); 689 throw new RuntimeException("Error creating Agent: '" + nameWithCorrectCase + "'"); 794 throw new RuntimeException("Error creating Agent: '" 795 + nameWithCorrectCase + "'"); 690 796 } 691 797 FrameGraphics.refresh(false); … … 705 811 706 812 /** 707 * Stops the currently running Agent (If there is one) by calling Agent.stop(). Note: This may not stop the Agent 708 * immediately, but the Agent should terminate as soon as it is safe to do so. 813 * Stops the currently running Agent (If there is one) by calling 814 * Agent.stop(). Note: This may not stop the Agent immediately, but the 815 * Agent should terminate as soon as it is safe to do so. 709 816 */ 710 817 public static void stopAgent() { … … 725 832 * 726 833 * @param launcher 727 * The Item used to launch the action, it may be required as a parameter 834 * The Item used to launch the action, it may be required as a 835 * parameter 728 836 * @param values 729 837 * A list of space separated String values to convert to objects 730 838 * @return The created array of Objects 731 839 */ 732 public static Object[] CreateObjects(Method method, Frame source, Item launcher, String values) { 840 public static Object[] CreateObjects(Method method, Frame source, 841 Item launcher, String values) { 733 842 // The parameter types that should be created from the given String 734 843 Class<?>[] paramTypes = method.getParameterTypes(); … … 743 852 744 853 /* 745 * if the first class in the list is a frame or item, it is the source or launcher length must be at least one746 * if we are still running854 * if the first class in the list is a frame or item, it is the source 855 * or launcher length must be at least one if we are still running 747 856 */ 748 857 if (paramTypes[ind] == Frame.class) { … … 792 901 793 902 /** 794 * Returns a string containing the remaining params after ignoring the first one. 903 * Returns a string containing the remaining params after ignoring the first 904 * one. 795 905 * 796 906 * @param params … … 824 934 825 935 /** 826 * Returns the first value in the space separated String of parameters passed in. Strings are enclosed in double827 * quotes.936 * Returns the first value in the space separated String of parameters 937 * passed in. Strings are enclosed in double quotes. 828 938 * 829 939 * @param params … … 854 964 855 965 /** 856 * Separates the name of the given command from any parameters and returns them 966 * Separates the name of the given command from any parameters and returns 967 * them Also deals with leading '@'s and leading and tailing whitespace. 857 968 * 858 969 * @param command … … 861 972 */ 862 973 private static String getName(String command) { 974 command = command.trim(); 975 if (command.startsWith("@")) 976 command = command.substring(1); 863 977 if (command.indexOf(" ") < 0) 864 978 return command; … … 868 982 869 983 /** 870 * Gets an uncapitalized font name and returns the capitalized font name. The capitalized form can be used with the 871 * Font.decoded method to get a corresponding Font object. 984 * Gets an uncapitalized font name and returns the capitalized font name. 985 * The capitalized form can be used with the Font.decoded method to get a 986 * corresponding Font object. 872 987 * 873 988 * @param fontName … … 886 1001 private static void initFonts() { 887 1002 if (_Fonts.size() == 0) { 888 String[] availableFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); 1003 String[] availableFonts = GraphicsEnvironment 1004 .getLocalGraphicsEnvironment() 1005 .getAvailableFontFamilyNames(); 889 1006 for (String s : availableFonts) { 890 1007 _Fonts.put(s.toLowerCase(), s); … … 898 1015 } 899 1016 900 public static Object PerformActionCatchErrors(Frame current, Item launcher, String command) { 1017 public static Object PerformActionCatchErrors(Frame current, Item launcher, 1018 String command) { 901 1019 try { 902 1020 return PerformAction(current, launcher, command); … … 906 1024 } catch (Exception e) { 907 1025 e.printStackTrace(); 908 MessageBay.errorMessage("Action failed: " + e.getClass().getSimpleName()); 1026 MessageBay.errorMessage("Action failed: " 1027 + e.getClass().getSimpleName()); 909 1028 } 910 1029 return null; -
trunk/src/org/expeditee/io/flowlayout/XGroupItem.java
r972 r975 37 37 import org.expeditee.items.Text; 38 38 39 40 public class XGroupItem extends XItem 41 { 42 public static final Text GROUPSEP_END = new Text('2' + ""); //ASCII '2' is start of text 43 44 public static final Text GROUPSEP_START = new Text('3' + ""); //ASCII '3' is end of text 45 46 enum FlowType { in_flow, out_of_flow_original, out_of_flow_faked_position }; 47 39 public class XGroupItem extends XItem { 40 public static final Text GROUPSEP_END = new Text('2' + ""); // ASCII '2' is 41 // start of text 42 43 public static final Text GROUPSEP_START = new Text('3' + ""); // ASCII '3' 44 // is end of 45 // text 46 47 enum FlowType { 48 in_flow, out_of_flow_original, out_of_flow_faked_position 49 }; 50 48 51 public static boolean doImplicitBoxing = true; 49 50 class MultiArrowHeadComparable implements Comparator<Item> {51 52 53 public int compare(Item o1, Item o2)54 {55 int o1y = o1.getY();56 int o2y = o2.getY(); 57 58 int order = 0; // default to assume they are identical 59 60 if (o1y<o2y) {61 order = -1; // done, o1 is higher up the frame than o2 => ourdefinition of 'before'62 }63 else if (o2y<o1y) {64 order = +1; // also done, o1 is lower down the frame than o2 =>our definition of 'after'65 66 }67 else {68 // have identical 'y' values, so look to 'x' to tie-break 69 70 int o1x = o1.getX();71 int o2x = o2.getX(); 72 73 if (o1x<o2x) {74 order = -1; // done, o1 is to the left of o2 => ourdefinition of 'before'75 }76 else if (o2x<o1x) {77 order = +1; // also done, o1 is to the right of o2 => ourdefinition of 'after'78 79 80 81 82 83 } 84 52 53 class MultiArrowHeadComparable implements Comparator<Item> { 54 55 @Override 56 public int compare(Item o1, Item o2) { 57 int o1y = o1.getY(); 58 int o2y = o2.getY(); 59 60 int order = 0; // default to assume they are identical 61 62 if (o1y < o2y) { 63 order = -1; // done, o1 is higher up the frame than o2 => our 64 // definition of 'before' 65 } else if (o2y < o1y) { 66 order = +1; // also done, o1 is lower down the frame than o2 => 67 // our definition of 'after' 68 69 } else { 70 // have identical 'y' values, so look to 'x' to tie-break 71 72 int o1x = o1.getX(); 73 int o2x = o2.getX(); 74 75 if (o1x < o2x) { 76 order = -1; // done, o1 is to the left of o2 => our 77 // definition of 'before' 78 } else if (o2x < o1x) { 79 order = +1; // also done, o1 is to the right of o2 => our 80 // definition of 'after' 81 } 82 } 83 84 return order; 85 } 86 } 87 85 88 Frame frame; 86 89 87 90 protected int y_span_height; 88 91 protected YOverlappingItemsSpan[] yitems_span_array; 89 92 90 93 List<Text> raw_text_item_list; 91 94 List<XGroupItem> grouped_item_list; 92 95 List<Item> remaining_item_list; 93 96 94 97 FlowType out_of_flow; 95 96 protected XGroupItem() 97 { 98 99 protected XGroupItem() { 98 100 frame = null; 99 101 100 102 y_span_height = 0; 101 103 yitems_span_array = null; 102 104 103 105 raw_text_item_list = null; 104 106 grouped_item_list = null; 105 107 remaining_item_list = null; 106 108 107 109 out_of_flow = FlowType.in_flow; 108 110 } 109 110 public XGroupItem(Frame frame, List<Item> y_ordered_items, Polygon enclosing_polygon)111 {111 112 public XGroupItem(Frame frame, List<Item> y_ordered_items, 113 Polygon enclosing_polygon) { 112 114 this.frame = frame; 113 this.out_of_flow = FlowType.in_flow;; 114 115 this.out_of_flow = FlowType.in_flow; 116 ; 117 115 118 if (enclosing_polygon == null) { 116 // e.g. when the top-level case, with no enclosing polygon at this stage 119 // e.g. when the top-level case, with no enclosing polygon at this 120 // stage 117 121 // => generate one based on the bounding box of the y_ordered_items 118 enclosing_polygon = DimensionExtent.boundingBoxPolygon(y_ordered_items); 119 } 120 121 Rectangle enclosing_bounding_rect = enclosing_polygon.getBounds(); 122 enclosing_polygon = DimensionExtent 123 .boundingBoxPolygon(y_ordered_items); 124 } 125 126 Rectangle enclosing_bounding_rect = enclosing_polygon.getBounds(); 122 127 initSpanArray(enclosing_bounding_rect); 123 124 // Step 1: Separate out the raw-text-items, the grouped-items and 'remaining' (e.g. points and lines) 125 128 129 // Step 1: Separate out the raw-text-items, the grouped-items and 130 // 'remaining' (e.g. points and lines) 131 126 132 raw_text_item_list = new ArrayList<Text>(); 127 133 grouped_item_list = new ArrayList<XGroupItem>(); 128 134 remaining_item_list = new ArrayList<Item>(); 129 130 separateYOverlappingItems(frame, y_ordered_items, enclosing_polygon, raw_text_item_list, grouped_item_list, remaining_item_list);131 132 135 136 separateYOverlappingItems(frame, y_ordered_items, enclosing_polygon, 137 raw_text_item_list, grouped_item_list, remaining_item_list); 138 133 139 // Step 2: Add in the raw-text items 134 140 for (Text text_item : raw_text_item_list) { 135 141 136 142 XRawItem x_raw_item = new XRawItem(text_item, this); 137 // overspill can occur (and is acceptable) when raw-text item spills out of enclosing shape (such as a rectangle) 143 // overspill can occur (and is acceptable) when raw-text item spills 144 // out of enclosing shape (such as a rectangle) 138 145 mapInItem(x_raw_item); 139 146 } 140 141 } 142 143 public XGroupItem(Frame frame, List<Item> y_ordered_items) 144 { 145 this(frame,y_ordered_items,null); 146 } 147 148 protected XGroupItem(XGroupItem imprint, Rectangle copy_to_bounding_rect) 149 { 147 148 } 149 150 public XGroupItem(Frame frame, List<Item> y_ordered_items) { 151 this(frame, y_ordered_items, null); 152 } 153 154 protected XGroupItem(XGroupItem imprint, Rectangle copy_to_bounding_rect) { 150 155 super(); 151 152 // Implement a shallow copy => share references to frame, and item list, and y-span 153 // Only the bounding box is changed => set to the 'copy_to_bounding_rect' passed in 154 156 157 // Implement a shallow copy => share references to frame, and item list, 158 // and y-span 159 // Only the bounding box is changed => set to the 160 // 'copy_to_bounding_rect' passed in 161 155 162 this.frame = imprint.frame; 156 163 157 164 y_span_height = imprint.y_span_height; 158 165 yitems_span_array = imprint.yitems_span_array; 159 160 this.raw_text_item_list 161 this.grouped_item_list 162 163 //int offX = imprint.getBoundingRect().x - copy_to_bounding_rect.x;164 //int offY = imprint.getBoundingRect().y - copy_to_bounding_rect.y;165 // this.grouped_item_list= new ArrayList<XGroupItem>();166 //for(XGroupItem newChild : imprint.grouped_item_list) {167 //Rectangle newRect = newChild.getBoundingRect();168 //newRect.x -= offX;169 //newRect.y -= offY;170 //this.grouped_item_list.add(new XGroupItem(newChild, newRect));171 //}166 167 this.raw_text_item_list = imprint.raw_text_item_list; 168 this.grouped_item_list = imprint.grouped_item_list; 169 170 // int offX = imprint.getBoundingRect().x - copy_to_bounding_rect.x; 171 // int offY = imprint.getBoundingRect().y - copy_to_bounding_rect.y; 172 // this.grouped_item_list = new ArrayList<XGroupItem>(); 173 // for(XGroupItem newChild : imprint.grouped_item_list) { 174 // Rectangle newRect = newChild.getBoundingRect(); 175 // newRect.x -= offX; 176 // newRect.y -= offY; 177 // this.grouped_item_list.add(new XGroupItem(newChild, newRect)); 178 // } 172 179 this.remaining_item_list = imprint.remaining_item_list; 173 174 this.out_of_flow = imprint.out_of_flow; // Or perhaps set it to FlowType.out_of_flow_fake_position straight away? 175 176 this.bounding_rect = new Rectangle(copy_to_bounding_rect); // deep copy to be on the safe side 177 } 178 179 180 protected void initSpanArray(Rectangle bounding_rect) 181 { 180 181 this.out_of_flow = imprint.out_of_flow; // Or perhaps set it to 182 // FlowType.out_of_flow_fake_position 183 // straight away? 184 185 this.bounding_rect = new Rectangle(copy_to_bounding_rect); // deep copy 186 // to be on 187 // the safe 188 // side 189 } 190 191 protected void initSpanArray(Rectangle bounding_rect) { 182 192 this.bounding_rect = bounding_rect; 183 193 184 194 int y_top = getBoundingYTop(); 185 195 int y_bot = getBoundingYBot(); 186 187 if (y_top <=y_bot) {196 197 if (y_top <= y_bot) { 188 198 // => have non-trivial span 189 190 y_span_height = (y_bot - y_top) + 2; // Polygon in Java excludes right and bottom sides so need extra "+1" in calc 191 192 yitems_span_array = new YOverlappingItemsSpan[y_span_height]; 193 } 194 else { 199 200 y_span_height = (y_bot - y_top) + 2; // Polygon in Java excludes 201 // right and bottom sides so 202 // need extra "+1" in calc 203 204 yitems_span_array = new YOverlappingItemsSpan[y_span_height]; 205 } else { 195 206 y_span_height = 0; 196 207 yitems_span_array = null; 197 208 } 198 209 } 199 210 200 211 public YOverlappingItemsSpan getSpanItemAt(int index) { 201 return yitems_span_array[index]; 202 } 203 204 public void setOutOfFlow(FlowType flow_type) 205 { 212 return yitems_span_array[index]; 213 } 214 215 public void setOutOfFlow(FlowType flow_type) { 206 216 out_of_flow = flow_type; 207 217 } 208 209 public FlowType getFlowItem() 210 { 218 219 public FlowType getFlowItem() { 211 220 return out_of_flow; 212 221 } 213 214 public boolean isOriginalOutOfFlowItem() 215 { 222 223 public boolean isOriginalOutOfFlowItem() { 216 224 return out_of_flow == FlowType.out_of_flow_original; 217 225 } 218 219 public boolean isFakedOutOfFlowItem() 220 { 226 227 public boolean isFakedOutOfFlowItem() { 221 228 return out_of_flow == FlowType.out_of_flow_faked_position; 222 229 } 223 230 224 225 public List<Text> getRawTextItemList() 226 { 231 public List<Text> getRawTextItemList() { 227 232 return raw_text_item_list; 228 233 } 229 234 230 235 public List<Text[]> getRawTextLines() { 231 236 final List<Text> rawText = getRawTextItemList(); 232 237 final List<Text> lineStarts = new LinkedList<Text>(); 233 for (final YOverlappingItemsSpan span : this.yitems_span_array) {234 if (span instanceof YOverlappingItemsTopEdge) {238 for (final YOverlappingItemsSpan span : this.yitems_span_array) { 239 if (span instanceof YOverlappingItemsTopEdge) { 235 240 final YOverlappingItemsTopEdge topEdge = (YOverlappingItemsTopEdge) span; 236 final List<XItem> xLine = topEdge.getXOrderedLine().getXItemList(); 237 for(final XItem xitem : xLine) { 238 if(xitem instanceof XRawItem) { 239 final Text item = (Text)((XRawItem) xitem).getItem(); 241 final List<XItem> xLine = topEdge.getXOrderedLine() 242 .getXItemList(); 243 for (final XItem xitem : xLine) { 244 if (xitem instanceof XRawItem) { 245 final Text item = (Text) ((XRawItem) xitem).getItem(); 240 246 lineStarts.add(item); 241 247 break; … … 245 251 } 246 252 final List<Integer> indexes = new LinkedList<Integer>(); 247 for (final Text lineStart : lineStarts)253 for (final Text lineStart : lineStarts) 248 254 indexes.add(rawText.indexOf(lineStart)); 249 255 final List<Text[]> ret = new LinkedList<Text[]>(); 250 256 int rangeEnd = indexes.get(0); 251 257 int last = rangeEnd; 252 for (int i = 1; i < indexes.size(); i++) {258 for (int i = 1; i < indexes.size(); i++) { 253 259 rangeEnd = indexes.get(i); 254 final List<Text> part = new LinkedList<Text>(rawText.subList(0, rangeEnd - last)); 260 final List<Text> part = new LinkedList<Text>(rawText.subList(0, 261 rangeEnd - last)); 255 262 last = rangeEnd; 256 263 rawText.removeAll(part); 257 part.toArray(new Text[] {});258 ret.add(part.toArray(new Text[] {}));259 } 260 ret.add(rawText.toArray(new Text[] {}));264 part.toArray(new Text[] {}); 265 ret.add(part.toArray(new Text[] {})); 266 } 267 ret.add(rawText.toArray(new Text[] {})); 261 268 return ret; 262 269 } 263 264 public List<XGroupItem> getGroupedItemList() 265 { 270 271 public List<XGroupItem> getGroupedItemList() { 266 272 return grouped_item_list; 267 273 } 268 269 public List<Item> getRemainingItemList() 270 { 274 275 public List<Item> getRemainingItemList() { 271 276 return remaining_item_list; 272 277 } 273 278 274 279 public int getItemSpanLength() { 275 280 return yitems_span_array.length; 276 281 } 277 278 public YOverlappingItemsSpan getYOverlappingItemsSpan(int y) 279 { 282 283 public YOverlappingItemsSpan getYOverlappingItemsSpan(int y) { 280 284 int y_index = y - getBoundingYTop(); 281 282 if ((y_index <0) || (y_index>=yitems_span_array.length)) {285 286 if ((y_index < 0) || (y_index >= yitems_span_array.length)) { 283 287 int y_top = getBoundingYTop(); 284 int y_bot = y_top + yitems_span_array.length -1; 285 286 System.err.println("Error in getYOverlappingItemsSpan(): index out of bounds for value " + y); 287 System.err.println(" => Operation mapped into local array: y-top=" + y_top + ", y-bot=" + y_bot); 288 289 return null; 288 int y_bot = y_top + yitems_span_array.length - 1; 289 290 System.err 291 .println("Error in getYOverlappingItemsSpan(): index out of bounds for value " 292 + y); 293 System.err.println(" => Operation mapped into local array: y-top=" 294 + y_top + ", y-bot=" + y_bot); 295 296 return null; 290 297 } 291 298 return yitems_span_array[y_index]; 292 299 } 293 300 294 301 protected int cropToTop(int y) { 295 302 int y_index = y - getBoundingYTop(); 296 297 if (y_index <0) {303 304 if (y_index < 0) { 298 305 y = getBoundingYTop(); 299 306 } 300 307 301 308 return y; 302 309 } 303 310 304 311 protected int cropToBot(int y) { 305 312 int y_index = y - getBoundingYTop(); 306 307 if (y_index >=yitems_span_array.length) {313 314 if (y_index >= yitems_span_array.length) { 308 315 y = getBoundingYBot(); 309 316 } 310 317 311 318 return y; 312 319 } 313 314 public void setYOverlappingItemsSpan(int y, YOverlappingItemsSpan yitems_span)315 {320 321 public void setYOverlappingItemsSpan(int y, 322 YOverlappingItemsSpan yitems_span) { 316 323 int y_index = y - getBoundingYTop(); 317 324 318 if ((y_index <0) || (y_index>=yitems_span_array.length)) {319 325 if ((y_index < 0) || (y_index >= yitems_span_array.length)) { 326 320 327 int y_top = getBoundingYTop(); 321 int y_bot = y_top + yitems_span_array.length -1; 322 323 System.err.println("Error in setYOverlappingItemsSpan(): index out of bounds for value " + y); 324 System.err.println(" => Operation mapped into local array: y-top=" + y_top + ", y-bot=" + y_bot); 325 326 return; 328 int y_bot = y_top + yitems_span_array.length - 1; 329 330 System.err 331 .println("Error in setYOverlappingItemsSpan(): index out of bounds for value " 332 + y); 333 System.err.println(" => Operation mapped into local array: y-top=" 334 + y_top + ", y-bot=" + y_bot); 335 336 return; 327 337 } 328 338 yitems_span_array[y_index] = yitems_span; 329 339 } 330 331 332 protected List<Item> followLinesToArrowHeads(Collection<Item> visited, Item anchor_item, List<Line>used_in_lines) 333 { 340 341 protected List<Item> followLinesToArrowHeads(Collection<Item> visited, 342 Item anchor_item, List<Line> used_in_lines) { 334 343 List<Item> arrow_head_endpoints = new ArrayList<Item>(); 335 336 for (Line line : used_in_lines) {337 344 345 for (Line line : used_in_lines) { 346 338 347 Item start_item = line.getStartItem(); 339 348 340 349 if (start_item == anchor_item) { 341 350 // the line we're considering is heading in the right direction 342 351 343 352 Item end_item = line.getEndItem(); 344 353 … … 348 357 349 358 List<Line> follow_lines = end_item.getLines(); 350 351 if (follow_lines.size() ==1) {359 360 if (follow_lines.size() == 1) { 352 361 // reached an end-point 353 362 if (end_item.hasVisibleArrow()) { 354 363 arrow_head_endpoints.add(end_item); 355 364 } 356 } 357 else if (follow_lines.size()>1) { 358 359 List<Item> followed_arrow_heads = followLinesToArrowHeads(visited,end_item,follow_lines);365 } else if (follow_lines.size() > 1) { 366 367 List<Item> followed_arrow_heads = followLinesToArrowHeads( 368 visited, end_item, follow_lines); 360 369 arrow_head_endpoints.addAll(followed_arrow_heads); 361 370 } 362 371 } 363 372 } 364 373 365 374 } 366 375 return arrow_head_endpoints; 367 376 } 368 369 370 protected XGroupItem innermostXGroup(Item arrow_head, List<XGroupItem> grouped_item_list) 371 { 377 378 protected XGroupItem innermostXGroup(Item arrow_head, 379 List<XGroupItem> grouped_item_list) { 372 380 XGroupItem innermost_item = null; 373 374 for (XGroupItem xgroup_item : grouped_item_list) {381 382 for (XGroupItem xgroup_item : grouped_item_list) { 375 383 if (xgroup_item.containsItem(arrow_head)) { 376 384 377 385 innermost_item = xgroup_item; 378 386 379 387 // Now see if it is in any of the nested ones? 380 388 381 389 List<XGroupItem> nested_group_item_list = xgroup_item.grouped_item_list; 382 383 XGroupItem potentially_better_item = innermostXGroup(arrow_head,nested_group_item_list); 384 390 391 XGroupItem potentially_better_item = innermostXGroup( 392 arrow_head, nested_group_item_list); 393 385 394 if (potentially_better_item != null) { 386 395 innermost_item = potentially_better_item; … … 388 397 break; 389 398 } 390 391 } 392 399 400 } 401 393 402 return innermost_item; 394 403 } 395 396 protected void removeXGroupItem(XGroupItem remove_item, List<XGroupItem> grouped_item_list)397 {398 399 for (XGroupItem xgroup_item : grouped_item_list) {400 404 405 protected void removeXGroupItem(XGroupItem remove_item, 406 List<XGroupItem> grouped_item_list) { 407 408 for (XGroupItem xgroup_item : grouped_item_list) { 409 401 410 if (xgroup_item == remove_item) { 402 411 grouped_item_list.remove(xgroup_item); 403 412 return; 404 } 405 else { 413 } else { 406 414 List<XGroupItem> nested_group_item_list = xgroup_item.grouped_item_list; 407 removeXGroupItem(remove_item,nested_group_item_list); 408 409 } 410 } 411 } 412 413 protected boolean daisyChainContainsLoop(final XGroupItem toplevel_xgroup, final Item exitingDot, Collection<XGroupItem> groupsSeen) { 415 removeXGroupItem(remove_item, nested_group_item_list); 416 417 } 418 } 419 } 420 421 protected boolean daisyChainContainsLoop(final XGroupItem toplevel_xgroup, 422 final Item exitingDot, Collection<XGroupItem> groupsSeen) { 414 423 final Item start_item = exitingDot; 415 424 final Item end_item = start_item.getLines().get(0).getEndItem(); 416 final XGroupItem xgroup = innermostXGroup(end_item,toplevel_xgroup.getGroupedItemList()); 425 final XGroupItem xgroup = innermostXGroup(end_item, 426 toplevel_xgroup.getGroupedItemList()); 417 427 418 if(groupsSeen.contains(xgroup)) return true; 419 428 if(xgroup == null) return false; 429 430 if (groupsSeen.contains(xgroup)) 431 return true; 432 420 433 groupsSeen.add(xgroup); 421 422 for (final Item i : xgroup.remaining_item_list) {434 435 for (final Item i : xgroup.remaining_item_list) { 423 436 final List<Line> lines = i.getLines(); 424 if (lines.size() == 1 && lines.get(0).getStartItem() == i) {437 if (lines.size() == 1 && lines.get(0).getStartItem() == i) { 425 438 return daisyChainContainsLoop(toplevel_xgroup, i, groupsSeen); 426 439 } 427 440 } 428 429 return false;430 } 431 432 protected void repositionOutOfFlowGroupsFollowLine(XGroupItem toplevel_xgroup, Item remaining_item, Collection<XGroupItem> out_of_flow)433 {441 return false; 442 } 443 444 protected void repositionOutOfFlowGroupsFollowLine( 445 XGroupItem toplevel_xgroup, Item remaining_item, 446 Collection<XGroupItem> out_of_flow) { 434 447 // See if this item is the start of a line 435 448 // Follow it if it is 436 // For each end of the line (potentially a multi-split poly-line) that has an arrow head: 437 // See if the arrow-head falls inside an XGroup area 438 // => Ignore endings that are in the same XGroupItem as the line started in 449 // For each end of the line (potentially a multi-split poly-line) that 450 // has an arrow head: 451 // See if the arrow-head falls inside an XGroup area 452 // => Ignore endings that are in the same XGroupItem as the line started 453 // in 439 454 List<Line> used_in_lines = remaining_item.getLines(); 440 if (used_in_lines.size() ==1) {455 if (used_in_lines.size() == 1) { 441 456 // at the start (or end) of a line 442 457 443 458 Item start_item = used_in_lines.get(0).getStartItem(); 444 459 … … 446 461 447 462 // found the start of a line 448 463 449 464 Collection<Item> visited = new HashSet<Item>(); 450 465 visited.add(remaining_item); 451 466 452 List<Item> arrow_head_endpoints = followLinesToArrowHeads(visited,remaining_item,used_in_lines); 453 454 //System.out.println("**** For Xgroup " + this + " with dot starting at " + remaining_item + " has " + arrow_head_endpoints.size() + " arrow head endpoints"); 455 456 Collections.sort(arrow_head_endpoints, new MultiArrowHeadComparable()); 457 458 for (Item arrow_head: arrow_head_endpoints) { 467 List<Item> arrow_head_endpoints = followLinesToArrowHeads( 468 visited, remaining_item, used_in_lines); 469 470 // System.out.println("**** For Xgroup " + this + 471 // " with dot starting at " + remaining_item + " has " + 472 // arrow_head_endpoints.size() + " arrow head endpoints"); 473 474 Collections.sort(arrow_head_endpoints, 475 new MultiArrowHeadComparable()); 476 477 for (Item arrow_head : arrow_head_endpoints) { 459 478 // find the inner-most group it falls within 460 461 List<XGroupItem> toplevel_grouped_item_list = toplevel_xgroup.getGroupedItemList(); 462 463 XGroupItem xgroup_item = innermostXGroup(arrow_head,toplevel_grouped_item_list); 464 479 480 List<XGroupItem> toplevel_grouped_item_list = toplevel_xgroup 481 .getGroupedItemList(); 482 483 XGroupItem xgroup_item = innermostXGroup(arrow_head, 484 toplevel_grouped_item_list); 485 465 486 if (xgroup_item != null) { 466 467 // Ignore if the found 'xgroup_item' is 'this' 468 // (i.e. the found arrow head is at the same XGroupItem level we are currently processing) 469 487 488 // Ignore if the found 'xgroup_item' is 'this' 489 // (i.e. the found arrow head is at the same XGroupItem 490 // level we are currently processing) 491 470 492 if (xgroup_item != this) { 471 472 if(/*out_of_flow.contains(xgroup_item) &&*/ daisyChainContainsLoop(toplevel_xgroup, start_item, new ArrayList<XGroupItem>())) { 473 System.err.println("#Found infinite loop; not following."); 493 494 if (/* out_of_flow.contains(xgroup_item) && */daisyChainContainsLoop( 495 toplevel_xgroup, start_item, 496 new ArrayList<XGroupItem>())) { 497 System.err.println("#Found infinite loop; not following. On frame: " + 498 start_item.getParent().getName() + " At position: " + 499 start_item.getPosition()); 474 500 continue; 475 501 } 476 477 478 479 if(!out_of_flow.contains(xgroup_item)) { 480 for (Item remaining_item_deeper: xgroup_item.getRemainingItemList()) { 481 xgroup_item.repositionOutOfFlowGroupsFollowLine(toplevel_xgroup,remaining_item_deeper,out_of_flow); 502 503 if (!out_of_flow.contains(xgroup_item)) { 504 for (Item remaining_item_deeper : xgroup_item 505 .getRemainingItemList()) { 506 xgroup_item 507 .repositionOutOfFlowGroupsFollowLine( 508 toplevel_xgroup, 509 remaining_item_deeper, 510 out_of_flow); 482 511 } 483 512 } 484 513 485 out_of_flow.add(xgroup_item); 486 487 488 // Can't delete here, as it causes a concurrent exception => add to 'out_of_flow' hashmap and perform removal later 489 490 //System.out.println("**** innermost XGroupItem = " + xgroup_item); 491 492 // Artificially rework its (x,y) org and dimension to make it appear where the start of the arrow is 493 494 Rectangle start_rect = start_item.getArea().getBounds(); 495 496 XGroupItem xgroup_item_shallow_copy = new XGroupItem(xgroup_item,start_rect); 497 498 // Perhaps the following two lines should be moved to inside the constructor?? 499 500 xgroup_item.setOutOfFlow(FlowType.out_of_flow_original); 501 xgroup_item_shallow_copy.setOutOfFlow(FlowType.out_of_flow_faked_position); 502 503 504 //xgroup_item.setBoundingRect(start_rect); 505 //xgroup_item.setOutOfFlow(); 506 507 // Finally add it in 508 //mapInItem(xgroup_item); 514 out_of_flow.add(xgroup_item); 515 516 // Can't delete here, as it causes a concurrent 517 // exception => add to 'out_of_flow' hashmap and 518 // perform removal later 519 520 // System.out.println("**** innermost XGroupItem = " 521 // + xgroup_item); 522 523 // Artificially rework its (x,y) org and dimension 524 // to make it appear where the start of the arrow is 525 526 Rectangle start_rect = start_item.getArea() 527 .getBounds(); 528 529 XGroupItem xgroup_item_shallow_copy = new XGroupItem( 530 xgroup_item, start_rect); 531 532 // Perhaps the following two lines should be moved 533 // to inside the constructor?? 534 535 xgroup_item 536 .setOutOfFlow(FlowType.out_of_flow_original); 537 xgroup_item_shallow_copy 538 .setOutOfFlow(FlowType.out_of_flow_faked_position); 539 540 // xgroup_item.setBoundingRect(start_rect); 541 // xgroup_item.setOutOfFlow(); 542 543 // Finally add it in 544 // mapInItem(xgroup_item); 509 545 mapInItem(xgroup_item_shallow_copy); 510 546 } … … 515 551 } 516 552 } 517 518 /** 519 * Look for any 'out-of-flow' XGroup boxes, signalled by the user drawing an arrow line to it 520 * => force an artificial change to such boxes so its dimensions becomes the starting point 521 * of the arrow line. This will make it sort to the desired spot in the YOverlapping span 522 */ 523 524 public void repositionOutOfFlowGroupsRecursive(XGroupItem toplevel_xgroup, Collection<XGroupItem> out_of_flow) 525 { 553 554 /** 555 * Look for any 'out-of-flow' XGroup boxes, signalled by the user drawing an 556 * arrow line to it => force an artificial change to such boxes so its 557 * dimensions becomes the starting point of the arrow line. This will make 558 * it sort to the desired spot in the YOverlapping span 559 */ 560 561 public void repositionOutOfFlowGroupsRecursive(XGroupItem toplevel_xgroup, 562 Collection<XGroupItem> out_of_flow) { 526 563 // Map in all the items in the given list: 527 for (Item remaining_item: remaining_item_list) { 528 529 repositionOutOfFlowGroupsFollowLine(toplevel_xgroup,remaining_item,out_of_flow); 530 } 531 532 // Now recursively work through each item's nested x-groups 533 for (XGroupItem xgroup_item: grouped_item_list) { 564 for (Item remaining_item : remaining_item_list) { 565 566 repositionOutOfFlowGroupsFollowLine(toplevel_xgroup, 567 remaining_item, out_of_flow); 568 } 569 570 // Now recursively work through each item's nested x-groups 571 for (XGroupItem xgroup_item : grouped_item_list) { 534 572 535 573 if (!out_of_flow.contains(xgroup_item)) { 536 xgroup_item.repositionOutOfFlowGroupsRecursive(toplevel_xgroup,out_of_flow); 537 } 538 } 539 } 540 541 542 public void repositionOutOfFlowGroups(XGroupItem toplevel_xgroup) 543 { 544 // Collection<XGroupItem> out_of_flow = new HashSet<XGroupItem>(); 574 xgroup_item.repositionOutOfFlowGroupsRecursive(toplevel_xgroup, 575 out_of_flow); 576 } 577 } 578 } 579 580 public void repositionOutOfFlowGroups(XGroupItem toplevel_xgroup) { 581 // Collection<XGroupItem> out_of_flow = new HashSet<XGroupItem>(); 545 582 Collection<XGroupItem> out_of_flow = new ArrayList<XGroupItem>(); 546 583 547 repositionOutOfFlowGroupsRecursive(toplevel_xgroup,out_of_flow); 548 549 // List<XGroupItem >toplevel_grouped_item_list = toplevel_xgroup.getGroupedItemList(); 584 repositionOutOfFlowGroupsRecursive(toplevel_xgroup, out_of_flow); 585 586 // List<XGroupItem >toplevel_grouped_item_list = 587 // toplevel_xgroup.getGroupedItemList(); 550 588 551 589 // **** 552 553 // Want to remove the original "out-of-position" blocks that were found, as (for each arrow 554 // point to such an original) shallow-copies now exist with 'faked' positions that correspond 590 591 // Want to remove the original "out-of-position" blocks that were found, 592 // as (for each arrow 593 // point to such an original) shallow-copies now exist with 'faked' 594 // positions that correspond 555 595 // to where the arrows start. 556 557 558 // No longer remove them from the nested grouped structure, rather, rely on these items being 559 // tagged "isOutOfFLow()" to be skipped by subsequent traversal calls (e.g., to generate 596 597 // No longer remove them from the nested grouped structure, rather, rely 598 // on these items being 599 // tagged "isOutOfFLow()" to be skipped by subsequent traversal calls 600 // (e.g., to generate 560 601 // the normal "in-flow" nested blocks) 561 562 // Structuring things this way, means that it is now possible to have multiple arrows 563 // pointing to the same block of code, and both "out-of-flow" arrows will be honoured, 564 // replicating the code at their respective start points 565 602 603 // Structuring things this way, means that it is now possible to have 604 // multiple arrows 605 // pointing to the same block of code, and both "out-of-flow" arrows 606 // will be honoured, 607 // replicating the code at their respective start points 608 566 609 /* 567 for (XGroupItem xgroup_item: out_of_flow) { 568 removeXGroupItem(xgroup_item,toplevel_grouped_item_list); 569 } 570 571 */ 572 573 } 574 610 * for (XGroupItem xgroup_item: out_of_flow) { 611 * removeXGroupItem(xgroup_item,toplevel_grouped_item_list); } 612 */ 613 614 } 615 575 616 /** 576 * Focusing only on enclosures that fit within the given polygon *and* have this item577 * in it, the method finds the largest of these enclosures and617 * Focusing only on enclosures that fit within the given polygon *and* have 618 * this item in it, the method finds the largest of these enclosures and 578 619 * returns all the items it contains 579 620 * 580 621 * @return 581 622 */ 582 public Collection<Item> getItemsInNestedEnclosure(Item given_item, AreaPolygon outer_polygon)583 {623 public Collection<Item> getItemsInNestedEnclosure(Item given_item, 624 AreaPolygon outer_polygon) { 584 625 Collection<Item> sameEnclosure = null; 585 626 Collection<Item> seen = new HashSet<Item>(); 586 627 587 628 double enclosureArea = 0.0; 588 629 589 630 for (Item i : frame.getItems()) { 590 591 // Go through all the enclosures ...592 631 632 // Go through all the enclosures ... 633 593 634 if (!seen.contains(i) && i.isEnclosed()) { 594 635 595 636 Collection<Item> i_enclosing_dots_coll = i.getEnclosingDots(); 596 List<Item> i_enclosing_dots = new ArrayList<Item>(i_enclosing_dots_coll); // Change to List of items 597 637 List<Item> i_enclosing_dots = new ArrayList<Item>( 638 i_enclosing_dots_coll); // Change to List of items 639 598 640 seen.addAll(i_enclosing_dots); 599 641 600 642 Polygon i_polygon = new Polygon(); 601 for (int di =0; di<i_enclosing_dots.size(); di++) {643 for (int di = 0; di < i_enclosing_dots.size(); di++) { 602 644 Item d = i_enclosing_dots.get(di); 603 645 604 646 i_polygon.addPoint(d.getX(), d.getY()); 605 647 } 606 607 // ... looking for one that is completely contained in the 'outer_polygon' and ... 648 649 // ... looking for one that is completely contained in the 650 // 'outer_polygon' and ... 608 651 if (outer_polygon.completelyContains(i_polygon)) { 609 652 610 653 Collection<Item> enclosed = i.getEnclosedItems(); 611 612 // ... includes this item 654 655 // ... includes this item 613 656 if (enclosed.contains(given_item)) { 614 657 615 // Remember it if it is larger than anything we've encountered before 658 // Remember it if it is larger than anything we've 659 // encountered before 616 660 if ((i.getEnclosedArea() > enclosureArea)) { 617 661 enclosureArea = i.getEnclosedArea(); … … 620 664 } 621 665 } 622 666 623 667 } 624 668 } … … 629 673 return sameEnclosure; 630 674 } 631 632 633 public void separateYOverlappingItems(Frame frame, List<Item> item_list, Polygon enclosing_polygon, 634 List<Text> raw_text_item_list, List<XGroupItem> grouped_item_list, List<Item> remaining_item_list) 635 { 675 676 public void separateYOverlappingItems(Frame frame, List<Item> item_list, 677 Polygon enclosing_polygon, List<Text> raw_text_item_list, 678 List<XGroupItem> grouped_item_list, List<Item> remaining_item_list) { 636 679 final List<Item> origonal_item_list = new ArrayList<Item>(item_list); 637 680 // raw_text_items_list => all the non-enclosed text items 638 // grouped_items_list => list of all enclosures containing text items 639 // remaining_item_list => whatever is left: mostly dots that form part of lines/polylines/polygons 681 // grouped_items_list => list of all enclosures containing text items 682 // remaining_item_list => whatever is left: mostly dots that form part 683 // of lines/polylines/polygons 640 684 641 685 AreaPolygon area_enclosing_polygon = new AreaPolygon(enclosing_polygon); 642 686 643 687 List<AreaPolygon> area_enclosed_polygon_list = new ArrayList<AreaPolygon>(); 644 688 645 while (item_list.size() >0) {689 while (item_list.size() > 0) { 646 690 Item item = item_list.remove(0); // shift 647 691 648 692 if (item instanceof Text) { 649 Text text = (Text)item; 650 651 Collection<Item> items_in_nested_enclosure_coll = getItemsInNestedEnclosure(text,area_enclosing_polygon); 652 List<Item> items_in_nested_enclosure = new ArrayList<Item>(items_in_nested_enclosure_coll); // Change to handling it as a list 653 654 if (items_in_nested_enclosure.size()==0) { 655 // if(text.getPixelBoundsUnion().x >= this.getBoundingXLeft() && text.getPixelBoundsUnion().y >= this.getBoundingYTop()) 656 // raw_text_item_list.add(text); 657 // else remaining_item_list.add(text); 693 Text text = (Text) item; 694 695 Collection<Item> items_in_nested_enclosure_coll = getItemsInNestedEnclosure( 696 text, area_enclosing_polygon); 697 List<Item> items_in_nested_enclosure = new ArrayList<Item>( 698 items_in_nested_enclosure_coll); // Change to handling 699 // it as a list 700 701 if (items_in_nested_enclosure.size() == 0) { 702 // if(text.getPixelBoundsUnion().x >= 703 // this.getBoundingXLeft() && text.getPixelBoundsUnion().y 704 // >= this.getBoundingYTop()) 705 // raw_text_item_list.add(text); 706 // else remaining_item_list.add(text); 658 707 raw_text_item_list.add(text); 659 } 660 else { 661 708 } else { 709 662 710 // Something other than just this text-item is around 663 664 while (items_in_nested_enclosure.size()>0) { 665 666 Item enclosure_item = items_in_nested_enclosure.remove(0); // shift 667 668 Polygon enclosed_polygon = enclosure_item.getEnclosedShape(); 669 670 if (enclosed_polygon!=null) { 671 // Got a group 711 712 while (items_in_nested_enclosure.size() > 0) { 713 714 Item enclosure_item = items_in_nested_enclosure 715 .remove(0); // shift 716 717 Polygon enclosed_polygon = enclosure_item 718 .getEnclosedShape(); 719 720 if (enclosed_polygon != null) { 721 // Got a group 672 722 // => Remove any items-in-nested-enclosure from: 673 723 // 674 // 'item_list' (so we don't waste our time discovering and processing again these related items) 675 // and 676 // 'raw_text_item_list' and 'remaining_item_lst' (as we now know they are part of the nested enclosed group) 677 678 AreaPolygon area_enclosed_polygon = new AreaPolygon(enclosed_polygon); 679 area_enclosed_polygon_list.add(area_enclosed_polygon); 724 // 'item_list' (so we don't waste our time 725 // discovering and processing again these related 726 // items) 727 // and 728 // 'raw_text_item_list' and 'remaining_item_lst' (as 729 // we now know they are part of the nested enclosed 730 // group) 731 732 AreaPolygon area_enclosed_polygon = new AreaPolygon( 733 enclosed_polygon); 734 area_enclosed_polygon_list 735 .add(area_enclosed_polygon); 680 736 681 737 item_list.remove(enclosure_item); 682 item_list.removeAll(items_in_nested_enclosure); 683 738 item_list.removeAll(items_in_nested_enclosure); 739 684 740 raw_text_item_list.remove(enclosure_item); 685 raw_text_item_list.removeAll(items_in_nested_enclosure); 686 741 raw_text_item_list 742 .removeAll(items_in_nested_enclosure); 743 687 744 remaining_item_list.remove(enclosure_item); 688 remaining_item_list.removeAll(items_in_nested_enclosure); 689 690 // Now remove any of the just used enclosed-items if they formed part of the perimeter 745 remaining_item_list 746 .removeAll(items_in_nested_enclosure); 747 748 // Now remove any of the just used enclosed-items if 749 // they formed part of the perimeter 691 750 List<Item> items_on_perimeter = new ArrayList<Item>(); 692 693 Iterator<Item> item_iterator = items_in_nested_enclosure.iterator(); 751 752 Iterator<Item> item_iterator = items_in_nested_enclosure 753 .iterator(); 694 754 while (item_iterator.hasNext()) { 695 755 Item item_to_check = item_iterator.next(); 696 Point pt_to_check = new Point(item_to_check.getX(),item_to_check.getY()); 697 698 if (area_enclosed_polygon.isPerimeterPoint(pt_to_check)) { 756 Point pt_to_check = new Point( 757 item_to_check.getX(), 758 item_to_check.getY()); 759 760 if (area_enclosed_polygon 761 .isPerimeterPoint(pt_to_check)) { 699 762 items_on_perimeter.add(item_to_check); 700 763 } 701 764 } 702 703 items_in_nested_enclosure.removeAll(items_on_perimeter); 765 766 items_in_nested_enclosure 767 .removeAll(items_on_perimeter); 704 768 } 705 769 706 770 else { 707 771 // Text or single point (with no enclosure) 708 772 709 773 // This item doesn't feature at this level 710 // => will be subsequently capture by the group polygon below 711 // and processed by the recursive call 712 774 // => will be subsequently capture by the group 775 // polygon below 776 // and processed by the recursive call 777 713 778 item_list.remove(enclosure_item); 714 779 } 715 780 } 716 781 717 782 } 718 783 … … 726 791 727 792 // Sort areas, smallest to largest 728 Collections.sort(area_enclosed_polygon_list, new Comparator<AreaPolygon>() { 729 730 public int compare(AreaPolygon ap1, AreaPolygon ap2) { 731 Double ap1_area = ap1.getArea(); 732 Double ap2_area = ap2.getArea(); 733 734 return ap2_area.compareTo(ap1_area); 735 } 736 }); 737 738 // Remove any enclosed polygon areas that are completely contained in a larger one 739 740 for (int ri=0; ri<area_enclosed_polygon_list.size(); ri++) { 793 Collections.sort(area_enclosed_polygon_list, 794 new Comparator<AreaPolygon>() { 795 796 public int compare(AreaPolygon ap1, AreaPolygon ap2) { 797 Double ap1_area = ap1.getArea(); 798 Double ap2_area = ap2.getArea(); 799 800 return ap2_area.compareTo(ap1_area); 801 } 802 }); 803 804 // Remove any enclosed polygon areas that are completely contained in a 805 // larger one 806 807 for (int ri = 0; ri < area_enclosed_polygon_list.size(); ri++) { 741 808 // ri = remove index pos 742 809 743 810 AreaPolygon rpoly = area_enclosed_polygon_list.get(ri); 744 811 745 for (int ci =ri+1; ci<area_enclosed_polygon_list.size(); ci++) {812 for (int ci = ri + 1; ci < area_enclosed_polygon_list.size(); ci++) { 746 813 // ci = check index pos 747 814 AreaPolygon cpoly = area_enclosed_polygon_list.get(ci); … … 753 820 } 754 821 } 755 756 757 // By this point, guaranteed that the remaining polygons are the largestones822 823 // By this point, guaranteed that the remaining polygons are the largest 824 // ones 758 825 // that capture groups of items 759 826 // 760 // There may be sub-groupings within them, which is the reason for the recursive call below 761 762 763 for (AreaPolygon area_polygon: area_enclosed_polygon_list) { 764 765 Collection<Item> enclosed_items = FrameUtils.getItemsEnclosedBy(frame, area_polygon); 766 List<Item> enclosed_item_list = new ArrayList<Item>(enclosed_items); 767 768 int i=0; 769 while (i<enclosed_item_list.size()) { 827 // There may be sub-groupings within them, which is the reason for the 828 // recursive call below 829 830 for (AreaPolygon area_polygon : area_enclosed_polygon_list) { 831 832 Collection<Item> enclosed_items = FrameUtils.getItemsEnclosedBy( 833 frame, area_polygon); 834 List<Item> enclosed_item_list = new ArrayList<Item>(enclosed_items); 835 836 int i = 0; 837 while (i < enclosed_item_list.size()) { 770 838 Item enclosed_item = enclosed_item_list.get(i); 771 772 // Filter out enclosed-items points that are part of the polygon's perimeter 839 840 // Filter out enclosed-items points that are part of the 841 // polygon's perimeter 773 842 if (area_polygon.isPerimeterPoint(enclosed_item.getPosition())) { 774 843 enclosed_item_list.remove(i); 775 //Don't include items the user hasn't asked us to. 776 } 777 //Only include items the user has asked to to include. 778 else if(!origonal_item_list.contains(enclosed_item)) enclosed_item_list.remove(i); 844 // Don't include items the user hasn't asked us to. 845 } 846 // Only include items the user has asked to to include. 847 else if (!origonal_item_list.contains(enclosed_item)) 848 enclosed_item_list.remove(i); 779 849 else { 780 850 i++; 781 851 } 782 852 } 783 853 784 854 // Recursively work on the identified sub-group 785 786 XGroupItem xgroup_item = new XGroupItem(frame, enclosed_item_list, area_polygon); 855 856 XGroupItem xgroup_item = new XGroupItem(frame, enclosed_item_list, 857 area_polygon); 787 858 788 859 grouped_item_list.add(xgroup_item); … … 790 861 } 791 862 792 793 794 protected void castShadowIntoEmptySpace(YOverlappingItemsTopEdge y_item_span_top_edge, int yt, int yb) 795 { 863 protected void castShadowIntoEmptySpace( 864 YOverlappingItemsTopEdge y_item_span_top_edge, int yt, int yb) { 796 865 // Assumes that all entries cast into are currently null 797 // => Use the more general castShadow() below, if this is not guaranteed to be the case 798 799 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(y_item_span_top_edge); 800 801 for (int y=yt; y<=yb; y++) { 802 803 setYOverlappingItemsSpan(y,y_span_shadow); 804 } 805 } 806 807 protected void castShadow(YOverlappingItemsTopEdge y_item_span_top_edge, int yt, int yb) 808 { 809 // Cast shadows only in places where there are currently no shadow entries 810 811 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(y_item_span_top_edge); 812 813 int y=yt; 814 815 while (y<=yb) { 866 // => Use the more general castShadow() below, if this is not guaranteed 867 // to be the case 868 869 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow( 870 y_item_span_top_edge); 871 872 for (int y = yt; y <= yb; y++) { 873 874 setYOverlappingItemsSpan(y, y_span_shadow); 875 } 876 } 877 878 protected void castShadow(YOverlappingItemsTopEdge y_item_span_top_edge, 879 int yt, int yb) { 880 // Cast shadows only in places where there are currently no shadow 881 // entries 882 883 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow( 884 y_item_span_top_edge); 885 886 int y = yt; 887 888 while (y <= yb) { 816 889 YOverlappingItemsSpan y_item_span = getYOverlappingItemsSpan(y); 817 818 if (y_item_span ==null) {819 setYOverlappingItemsSpan(y, y_span_shadow);890 891 if (y_item_span == null) { 892 setYOverlappingItemsSpan(y, y_span_shadow); 820 893 y++; 821 } 822 else if (y_item_span instanceof YOverlappingItemsTopEdge) { 823 // Our shadow has run into another top-edged zone 824 // => need to extend the shadow to include this zone as well 825 // (making this encountered top-edge obsolete) 826 894 } else if (y_item_span instanceof YOverlappingItemsTopEdge) { 895 // Our shadow has run into another top-edged zone 896 // => need to extend the shadow to include this zone as well 897 // (making this encountered top-edge obsolete) 898 827 899 // Merge the newly encountered top-line in with the current one 828 900 // and change all its shadow references to our (dominant) one 829 830 XOrderedLine dominant_x_line = y_item_span_top_edge.getXOrderedLine(); 831 832 YOverlappingItemsTopEdge obsolete_top_edge = (YOverlappingItemsTopEdge)y_item_span; 833 XOrderedLine obsolete_x_line = obsolete_top_edge.getXOrderedLine(); 834 835 for (XItem xitem: obsolete_x_line.getXItemList()) { 836 901 902 XOrderedLine dominant_x_line = y_item_span_top_edge 903 .getXOrderedLine(); 904 905 YOverlappingItemsTopEdge obsolete_top_edge = (YOverlappingItemsTopEdge) y_item_span; 906 XOrderedLine obsolete_x_line = obsolete_top_edge 907 .getXOrderedLine(); 908 909 for (XItem xitem : obsolete_x_line.getXItemList()) { 910 837 911 dominant_x_line.orderedMergeItem(xitem); 838 912 } 839 913 840 914 while (y_item_span != null) { 841 842 setYOverlappingItemsSpan(y, y_span_shadow);843 915 916 setYOverlappingItemsSpan(y, y_span_shadow); 917 844 918 y++; 845 846 if (y <=yb) {919 920 if (y <= yb) { 847 921 y_item_span = getYOverlappingItemsSpan(y); 848 } 849 else { 922 } else { 850 923 y_item_span = null; 851 924 } 852 925 } 853 } 854 else { 926 } else { 855 927 y++; 856 928 } 857 929 } 858 930 } 859 860 protected int autocropToTop(XItem xitem) 861 { 862 // top-edge over-spill can occur (and is acceptable) when (for example) the given xitem is a raw-text item 863 // that doesn't neatly fit in an enclosed area (i,e., is poking out the top of an enclosing rectangle) 931 932 protected int autocropToTop(XItem xitem) { 933 // top-edge over-spill can occur (and is acceptable) when (for example) 934 // the given xitem is a raw-text item 935 // that doesn't neatly fit in an enclosed area (i,e., is poking out the 936 // top of an enclosing rectangle) 864 937 // => cut down to the top of 'this' xgroupitem 865 938 866 939 int yt = xitem.getBoundingYTop(); 867 868 940 869 941 yt = cropToTop(yt); // Only changes yt if in an over-spill situation 870 942 871 943 return yt; 872 873 } 874 875 protected int autocropToBot(XItem xitem) 876 { 877 878 // similar to autocropToTop(), bottom-edge over-spill can occur (and is acceptable) with879 // a less than precise placed raw-text item 880 944 945 } 946 947 protected int autocropToBot(XItem xitem) { 948 949 // similar to autocropToTop(), bottom-edge over-spill can occur (and is 950 // acceptable) with 951 // a less than precise placed raw-text item 952 881 953 int yb = xitem.getBoundingYBot(); 882 954 883 955 yb = cropToBot(yb); // Only changes yb if in an over-spill situation 884 956 885 957 return yb; 886 958 } 887 888 protected boolean yExtentIntersection(XGroupItem xgroup_item1, XGroupItem xgroup_item2)889 {959 960 protected boolean yExtentIntersection(XGroupItem xgroup_item1, 961 XGroupItem xgroup_item2) { 890 962 // Computation applied to y-extent of each rectangle 891 892 // To intersect, either a point in item1 falls inside item2, 893 // or else item2 is completely within item1 894 895 963 964 // To intersect, either a point in item1 falls inside item2, 965 // or else item2 is completely within item1 966 896 967 int yt1 = xgroup_item1.getBoundingYTop(); 897 968 int yb1 = xgroup_item1.getBoundingYBot(); 898 969 899 970 int yt2 = xgroup_item2.getBoundingYTop(); 900 971 901 972 // yt1 insides item2? 902 973 if (xgroup_item2.containedInYExtent(yt1)) { 903 974 return true; 904 975 } 905 976 906 977 // yb1 inside item2? 907 978 if (xgroup_item2.containedInYExtent(yb1)) { 908 979 return true; 909 980 } 910 981 911 982 // item2 completely inside item1? 912 983 // … … 914 985 // by checking only one of the values is inside. 915 986 // Doesn't matter which => choose yt2 916 987 917 988 if (xgroup_item1.containedInYExtent(yt2)) { 918 989 return true; 919 990 } 920 991 921 992 // Get to here if there is no intersection 922 993 return false; 923 924 } 925 protected ArrayList<XGroupItem> calcXGroupYExtentIntersection(XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) 926 { 994 995 } 996 997 protected ArrayList<XGroupItem> calcXGroupYExtentIntersection( 998 XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) { 927 999 ArrayList<XGroupItem> intersect_list = new ArrayList<XGroupItem>(); 928 929 for (XGroupItem xgroup_item : xgroup_item_list) {1000 1001 for (XGroupItem xgroup_item : xgroup_item_list) { 930 1002 931 1003 if (xgroup_item == xgroup_pivot_item) { … … 934 1006 } 935 1007 936 if (yExtentIntersection(xgroup_pivot_item, xgroup_item)) {1008 if (yExtentIntersection(xgroup_pivot_item, xgroup_item)) { 937 1009 intersect_list.add(xgroup_item); 938 1010 } 939 1011 } 940 1012 941 1013 return intersect_list; 942 1014 } 943 944 protected ArrayList<YOverlappingItemsTopEdge> calcYSpanOverlap(XItem xitem) 945 { 1015 1016 protected ArrayList<YOverlappingItemsTopEdge> calcYSpanOverlap(XItem xitem) { 946 1017 int yt = autocropToTop(xitem); 947 1018 int yb = autocropToBot(xitem); 948 949 1019 950 1020 ArrayList<YOverlappingItemsTopEdge> top_edge_list = new ArrayList<YOverlappingItemsTopEdge>(); 951 952 for (int y =yt; y<=yb; y++) {1021 1022 for (int y = yt; y <= yb; y++) { 953 1023 954 1024 YOverlappingItemsSpan item_span = getYOverlappingItemsSpan(y); … … 958 1028 if (item_span instanceof YOverlappingItemsTopEdge) { 959 1029 960 YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span;961 1030 YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span; 1031 962 1032 top_edge_list.add(y_item_span_top_edge); 963 1033 } … … 965 1035 966 1036 } 967 1037 968 1038 return top_edge_list; 969 1039 } 970 971 972 protected boolean multipleYSpanOverlap(XItem xitem) 973 { 1040 1041 protected boolean multipleYSpanOverlap(XItem xitem) { 974 1042 return calcYSpanOverlap(xitem).size() > 0; 975 1043 } 976 977 public void mapInItem(XItem xitem) 978 { 979 1044 1045 public void mapInItem(XItem xitem) { 1046 980 1047 int yt = autocropToTop(xitem); 981 1048 int yb = autocropToBot(xitem); 982 1049 /* 983 int yt = xitem.getBoundingYTop(); 984 int yb = xitem.getBoundingYBot(); 985 986 // top-edge over-spill can occur (and is acceptable) when (for example) the given xitem is a raw-text item 987 // that doesn't neatly fit in an enclosed area (i,e., is poking out the top of an enclosing rectangle) 988 yt = cropToTop(yt); // Only changes yt if in an over-spill situation 989 990 // similarly, bottom-edge over-spill can occur (and is acceptable) with a less than precise placed raw-text item 991 yb = cropToBot(yb); // Only changes yb if in an over-spill situation 992 */ 993 994 // Merge into 'items_span' checking for overlaps (based on xitem's y-span) 995 1050 * int yt = xitem.getBoundingYTop(); int yb = xitem.getBoundingYBot(); 1051 * 1052 * // top-edge over-spill can occur (and is acceptable) when (for 1053 * example) the given xitem is a raw-text item // that doesn't neatly 1054 * fit in an enclosed area (i,e., is poking out the top of an enclosing 1055 * rectangle) yt = cropToTop(yt); // Only changes yt if in an over-spill 1056 * situation 1057 * 1058 * // similarly, bottom-edge over-spill can occur (and is acceptable) 1059 * with a less than precise placed raw-text item yb = cropToBot(yb); // 1060 * Only changes yb if in an over-spill situation 1061 */ 1062 1063 // Merge into 'items_span' checking for overlaps (based on xitem's 1064 // y-span) 1065 996 1066 boolean merged_item = false; 997 for (int y =yt; y<=yb; y++) {1067 for (int y = yt; y <= yb; y++) { 998 1068 999 1069 YOverlappingItemsSpan item_span = getYOverlappingItemsSpan(y); … … 1006 1076 1007 1077 // Need to: 1008 // 1. *Required* Insert new item into current x-ordered line 1009 // 2. *Conditionally* Move entire x-ordered line up to higher 'y' top edge 1010 // 3. *Required* Cast shadow for new item 1078 // 1. *Required* Insert new item into current x-ordered line 1079 // 2. *Conditionally* Move entire x-ordered line up to 1080 // higher 'y' top edge 1081 // 3. *Required* Cast shadow for new item 1011 1082 1012 1083 // Note for Step 2: 1013 // i) No need to do the move if y == yt 1014 // (the case when the new top edge is exactly the same height as the existing one) 1015 // ii) If moving top-edge (the case when y > yt) then no need to recalculate the top edge for existing shadows, as 1016 // this 'connection' is stored as a reference 1017 1084 // i) No need to do the move if y == yt 1085 // (the case when the new top edge is exactly the same 1086 // height as the existing one) 1087 // ii) If moving top-edge (the case when y > yt) then no 1088 // need to recalculate the top edge for existing shadows, as 1089 // this 'connection' is stored as a reference 1018 1090 1019 1091 // Step 1: insert into existing top-edge 1020 1092 1021 YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge)item_span; 1022 XOrderedLine xitem_span = y_item_span_top_edge.getXOrderedLine(); 1023 1024 xitem_span.orderedMergeItem(xitem.getBoundingXLeft(),xitem); 1025 1026 // Step 2: if our new top-edge is higher than the existing one, then need to move existing top-edge 1027 if (y>yt) { 1093 YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span; 1094 XOrderedLine xitem_span = y_item_span_top_edge 1095 .getXOrderedLine(); 1096 1097 xitem_span 1098 .orderedMergeItem(xitem.getBoundingXLeft(), xitem); 1099 1100 // Step 2: if our new top-edge is higher than the existing 1101 // one, then need to move existing top-edge 1102 if (y > yt) { 1028 1103 1029 1104 // Move to new position … … 1031 1106 1032 1107 // Old position needs to become a shadow reference 1033 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(y_item_span_top_edge); 1034 setYOverlappingItemsSpan(y,y_span_shadow); 1108 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow( 1109 y_item_span_top_edge); 1110 setYOverlappingItemsSpan(y, y_span_shadow); 1035 1111 } 1036 1112 1037 1038 1113 // Step 3: Cast shadow 1039 castShadow(y_item_span_top_edge, yt+1, yb); 1040 1041 } 1042 else { 1043 // Top edge to our new item has hit a shadow entry (straight off) 1044 // => Look up what the shadow references, and then add in to that 1045 1046 // Effectively after the shadow reference lookup this is the same 1047 // as the above, without the need to worry about Step 2 (as no move is needed) 1048 1049 YOverlappingItemsShadow y_item_span_shadow = (YOverlappingItemsShadow)item_span; 1050 YOverlappingItemsTopEdge y_item_span_top_edge = y_item_span_shadow.getTopEdge(); 1051 1052 XOrderedLine xitem_span = y_item_span_top_edge.getXOrderedLine(); 1114 castShadow(y_item_span_top_edge, yt + 1, yb); 1115 1116 } else { 1117 // Top edge to our new item has hit a shadow entry (straight 1118 // off) 1119 // => Look up what the shadow references, and then add in to 1120 // that 1121 1122 // Effectively after the shadow reference lookup this is the 1123 // same 1124 // as the above, without the need to worry about Step 2 (as 1125 // no move is needed) 1126 1127 YOverlappingItemsShadow y_item_span_shadow = (YOverlappingItemsShadow) item_span; 1128 YOverlappingItemsTopEdge y_item_span_top_edge = y_item_span_shadow 1129 .getTopEdge(); 1130 1131 XOrderedLine xitem_span = y_item_span_top_edge 1132 .getXOrderedLine(); 1053 1133 1054 1134 // merge with this item list, preserving x ordering 1055 xitem_span.orderedMergeItem(xitem.getBoundingXLeft(),xitem); 1135 xitem_span 1136 .orderedMergeItem(xitem.getBoundingXLeft(), xitem); 1056 1137 1057 1138 // Now Cast shadow 1058 castShadow(y_item_span_top_edge, yt +1, yb);1139 castShadow(y_item_span_top_edge, yt + 1, yb); 1059 1140 } 1060 1141 … … 1066 1147 } 1067 1148 1068 // Having checked all the y-location's ('yt' to 'yb') of this x-item, if all y-span entries were found to be null 1149 // Having checked all the y-location's ('yt' to 'yb') of this x-item, if 1150 // all y-span entries were found to be null 1069 1151 // => 'merged_item' is still false 1070 1152 1071 1153 if (!merged_item) { 1072 // xitem didn't intersect with any existing y-spans 1073 // => simple case for add (i.e. all entries affected by map are currently null) 1074 1075 // Start up a new x-ordered-line (consisting of only 'xitem'), add in top edge and cast shadow 1076 1154 // xitem didn't intersect with any existing y-spans 1155 // => simple case for add (i.e. all entries affected by map are 1156 // currently null) 1157 1158 // Start up a new x-ordered-line (consisting of only 'xitem'), add 1159 // in top edge and cast shadow 1160 1077 1161 XOrderedLine xitem_line = new XOrderedLine(xitem); 1078 1079 YOverlappingItemsTopEdge y_item_span_top_edge = new YOverlappingItemsTopEdge( xitem_line);1080 1081 setYOverlappingItemsSpan(yt,y_item_span_top_edge); 1082 1083 castShadowIntoEmptySpace(y_item_span_top_edge, yt+1, yb); 1084 }1085 1086 1087 } 1088 1089 private ArrayList<DimensionExtent> calcXGroupXGaps( XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list)1090 {1162 1163 YOverlappingItemsTopEdge y_item_span_top_edge = new YOverlappingItemsTopEdge( 1164 xitem_line); 1165 1166 setYOverlappingItemsSpan(yt, y_item_span_top_edge); 1167 1168 castShadowIntoEmptySpace(y_item_span_top_edge, yt + 1, yb); 1169 } 1170 1171 } 1172 1173 private ArrayList<DimensionExtent> calcXGroupXGaps( 1174 XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) { 1091 1175 // 'xgroup_pivot_item' current not used!! 1092 1176 1093 1177 int enclosing_xl = getBoundingXLeft(); 1094 1178 int enclosing_xr = getBoundingXRight(); 1095 1096 int enclosing_x_dim = enclosing_xr - enclosing_xl +1; 1097 boolean is_x_shadow[] = new boolean[enclosing_x_dim]; // defaults all values to false 1098 1179 1180 int enclosing_x_dim = enclosing_xr - enclosing_xl + 1; 1181 boolean is_x_shadow[] = new boolean[enclosing_x_dim]; // defaults all 1182 // values to 1183 // false 1184 1099 1185 ArrayList<DimensionExtent> x_gap_list = new ArrayList<DimensionExtent>(); 1100 1101 for (XGroupItem xgroup_item : xgroup_item_list) {1186 1187 for (XGroupItem xgroup_item : xgroup_item_list) { 1102 1188 int xl = xgroup_item.getBoundingXLeft(); 1103 1189 int xr = xgroup_item.getBoundingXRight(); 1104 1105 for (int x =xl; x<=xr; x++) {1190 1191 for (int x = xl; x <= xr; x++) { 1106 1192 int x_offset = x - enclosing_xl; 1107 1193 is_x_shadow[x_offset] = true; 1108 1194 } 1109 1195 } 1110 1111 // New find where the contiguous runs of 'false' are, as they point to the gaps 1112 1196 1197 // New find where the contiguous runs of 'false' are, as they point to 1198 // the gaps 1199 1113 1200 int x = 1; 1114 1201 int start_x_gap = enclosing_xl; 1115 1202 boolean in_gap = true; 1116 1203 1117 1204 while (x < is_x_shadow.length) { 1118 boolean prev_is_shadow = is_x_shadow[x -1];1205 boolean prev_is_shadow = is_x_shadow[x - 1]; 1119 1206 boolean this_is_shadow = is_x_shadow[x]; 1120 1207 … … 1123 1210 start_x_gap = enclosing_xl + x; 1124 1211 in_gap = true; 1125 } 1126 else if (!prev_is_shadow && this_is_shadow) { 1212 } else if (!prev_is_shadow && this_is_shadow) { 1127 1213 // reached the end of a gap, record it 1128 int end_x_gap = enclosing_xl + x - 1;1129 DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap);1214 int end_x_gap = enclosing_xl + x - 1; 1215 DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap); 1130 1216 x_gap_list.add(de); 1131 1217 in_gap = false; … … 1133 1219 x++; 1134 1220 } 1135 1221 1136 1222 if (in_gap) { 1137 int end_x_gap = enclosing_xl + x - 1;1138 DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap);1223 int end_x_gap = enclosing_xl + x - 1; 1224 DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap); 1139 1225 x_gap_list.add(de); 1140 1226 } 1141 1227 1142 1228 return x_gap_list; 1143 1229 } 1144 1230 1145 protected void boxMultipleXRawItemsInGaps(XGroupItem xgroup_pivot_item, ArrayList<DimensionExtent> x_text_gap_list)1146 {1231 protected void boxMultipleXRawItemsInGaps(XGroupItem xgroup_pivot_item, 1232 ArrayList<DimensionExtent> x_text_gap_list) { 1147 1233 ArrayList<YOverlappingItemsTopEdge> y_span_overlap = calcYSpanOverlap(xgroup_pivot_item); 1148 1149 // work out where continuous runs of raw-text items intersect with the gaps occurring between boxed items1150 1151 // foreach x-gap, 1152 // foreach y-span's list of x-sorted objects,1153 // => see how many raw-text items fit into it1154 1155 1234 1235 // work out where continuous runs of raw-text items intersect with the 1236 // gaps occurring between boxed items 1237 1238 // foreach x-gap, 1239 // foreach y-span's list of x-sorted objects, 1240 // => see how many raw-text items fit into it 1241 1156 1242 ArrayList<ArrayList<XRawItem>> implicit_box_list = new ArrayList<ArrayList<XRawItem>>(); 1157 1158 for (DimensionExtent de_gap : x_text_gap_list) {1159 1243 1244 for (DimensionExtent de_gap : x_text_gap_list) { 1245 1160 1246 int deg_xl = de_gap.min; 1161 1247 int deg_xr = de_gap.max; 1162 1248 1163 1249 ArrayList<XRawItem> grouped_raw_text_list = new ArrayList<XRawItem>(); 1164 1250 1165 1251 int y_span_line_count = 0; 1166 1167 for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) {1168 1252 1253 for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) { 1254 1169 1255 List<XItem> x_item_list = y_top_edge.x_items.getXItemList(); 1170 1256 1171 1257 boolean used_x_raw_text = false; 1172 1173 for (XItem x_item : x_item_list) {1174 1258 1259 for (XItem x_item : x_item_list) { 1260 1175 1261 if (x_item instanceof XRawItem) { 1176 XRawItem x_raw_item = (XRawItem) x_item;1177 1262 XRawItem x_raw_item = (XRawItem) x_item; 1263 1178 1264 int xri_xl = x_raw_item.getBoundingXLeft(); 1179 1265 int xri_xr = x_raw_item.getBoundingXRight(); 1180 1181 if ((xri_xl >=deg_xl) && (xri_xr<=deg_xr)) {1266 1267 if ((xri_xl >= deg_xl) && (xri_xr <= deg_xr)) { 1182 1268 grouped_raw_text_list.add(x_raw_item); 1183 1269 used_x_raw_text = true; … … 1185 1271 } 1186 1272 } 1187 1273 1188 1274 if (used_x_raw_text) { 1189 1275 y_span_line_count++; 1190 1276 } 1191 1277 } 1192 1193 // Only interested in implicitly boxing if the formed group is used over 2 or more y-span lines 1194 if (y_span_line_count>=2) { 1278 1279 // Only interested in implicitly boxing if the formed group is used 1280 // over 2 or more y-span lines 1281 if (y_span_line_count >= 2) { 1195 1282 implicit_box_list.add(grouped_raw_text_list); 1196 1283 } 1197 1284 } 1198 1199 //System.err.println("*** Number of implicit groups found: " + implicit_box_list.size()); 1200 1285 1286 // System.err.println("*** Number of implicit groups found: " + 1287 // implicit_box_list.size()); 1288 1201 1289 for (ArrayList<XRawItem> implicit_x_item_list : implicit_box_list) { 1202 1203 for (YOverlappingItemsTopEdge y_top_edge: y_span_overlap) { 1204 1205 List<XItem> yspan_x_item_list = y_top_edge.x_items.getXItemList(); 1206 1290 1291 for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) { 1292 1293 List<XItem> yspan_x_item_list = y_top_edge.x_items 1294 .getXItemList(); 1295 1207 1296 // Remove all the XRawItems from the current y-span 1208 1297 yspan_x_item_list.removeAll(implicit_x_item_list); 1209 1210 } 1211 1298 1299 } 1300 1212 1301 // Create a list of original Text items 1213 1302 // (OK that they are not y-ordered) 1214 ArrayList<Item> implicit_item_list = new ArrayList<Item>();1215 for (XRawItem x_raw_item : implicit_x_item_list) {1303 ArrayList<Item> implicit_item_list = new ArrayList<Item>(); 1304 for (XRawItem x_raw_item : implicit_x_item_list) { 1216 1305 Item item = (Text) x_raw_item.item; 1217 1306 implicit_item_list.add(item); … … 1219 1308 1220 1309 // Now insert them as their own boxed up items 1221 XGroupItem implicit_xgroup = new XGroupItem(frame,implicit_item_list); 1310 XGroupItem implicit_xgroup = new XGroupItem(frame, 1311 implicit_item_list); 1222 1312 this.mapInItem(implicit_xgroup); 1223 1313 } 1224 1225 } 1226 1227 protected void implicitBoxing(XGroupItem xgroup_pivot_item,List<XGroupItem> xgroup_item_list) { 1228 1229 // Implicit boxing is needed if there are two or more raw-text items 1314 1315 } 1316 1317 protected void implicitBoxing(XGroupItem xgroup_pivot_item, 1318 List<XGroupItem> xgroup_item_list) { 1319 1320 // Implicit boxing is needed if there are two or more raw-text items 1230 1321 // that overlap in y-span-map of the given xgroup_item 1231 1322 1232 1323 // First establish if there is any kind of multiple overlap 1233 1324 if (multipleYSpanOverlap(xgroup_pivot_item)) { 1234 1235 // Overlap could be with other boxed items, so need to investigate further 1236 1237 // Do this by working out if there are any raw-text items lurking between the pivot xgroup_item 1238 // and the list of other xgroup_items 1239 1240 // Step 1: Get list intersection (y-dim) of this group item, with any 1241 // other group items in xgroup_item_list 1242 ArrayList<XGroupItem> y_overlapping_xgroup_item_list = calcXGroupYExtentIntersection(xgroup_pivot_item,xgroup_item_list); 1243 1244 // Step 2: Work out where the gaps are between the y-overlapping boxes in the x-dimension 1245 ArrayList<DimensionExtent> x_text_gap_list = calcXGroupXGaps(xgroup_pivot_item,y_overlapping_xgroup_item_list); 1246 1247 // Step 3: Remove any sequences of raw-text items that fall in the gaps from YSpan, box them up, and reinsert 1248 boxMultipleXRawItemsInGaps(xgroup_pivot_item,x_text_gap_list); 1249 1250 } 1251 } 1252 1253 1254 1255 public void mapInXGroupItemsRecursive(List<XGroupItem> xgroup_item_list) 1256 { 1257 1325 1326 // Overlap could be with other boxed items, so need to investigate 1327 // further 1328 1329 // Do this by working out if there are any raw-text items lurking 1330 // between the pivot xgroup_item 1331 // and the list of other xgroup_items 1332 1333 // Step 1: Get list intersection (y-dim) of this group item, with 1334 // any 1335 // other group items in xgroup_item_list 1336 ArrayList<XGroupItem> y_overlapping_xgroup_item_list = calcXGroupYExtentIntersection( 1337 xgroup_pivot_item, xgroup_item_list); 1338 1339 // Step 2: Work out where the gaps are between the y-overlapping 1340 // boxes in the x-dimension 1341 ArrayList<DimensionExtent> x_text_gap_list = calcXGroupXGaps( 1342 xgroup_pivot_item, y_overlapping_xgroup_item_list); 1343 1344 // Step 3: Remove any sequences of raw-text items that fall in the 1345 // gaps from YSpan, box them up, and reinsert 1346 boxMultipleXRawItemsInGaps(xgroup_pivot_item, x_text_gap_list); 1347 1348 } 1349 } 1350 1351 public void mapInXGroupItemsRecursive(List<XGroupItem> xgroup_item_list) { 1352 1258 1353 // Map in all the items in the given list: 1259 for (XGroupItem xgroup_item : xgroup_item_list) {1354 for (XGroupItem xgroup_item : xgroup_item_list) { 1260 1355 if (!xgroup_item.isOriginalOutOfFlowItem()) { 1261 1356 1262 1357 // See if any implicit boxing of raw-text items is needed 1263 1358 if (doImplicitBoxing) { 1264 implicitBoxing(xgroup_item, xgroup_item_list);1359 implicitBoxing(xgroup_item, xgroup_item_list); 1265 1360 } 1266 1361 mapInItem(xgroup_item); // Map in this x-group-item 1267 1362 } 1268 } 1269 1270 // Now recursively work through each item's nested x-groups 1271 for (XGroupItem xgroup_item: xgroup_item_list) { 1272 1273 List<XGroupItem> nested_xgroup_item_list = xgroup_item.getGroupedItemList(); 1274 1275 if (nested_xgroup_item_list.size() >0) { 1363 } 1364 1365 // Now recursively work through each item's nested x-groups 1366 for (XGroupItem xgroup_item : xgroup_item_list) { 1367 1368 List<XGroupItem> nested_xgroup_item_list = xgroup_item 1369 .getGroupedItemList(); 1370 1371 if (nested_xgroup_item_list.size() > 0) { 1276 1372 xgroup_item.mapInXGroupItemsRecursive(nested_xgroup_item_list); 1277 1373 } 1278 } 1279 } 1280 1374 } 1375 } 1376 1281 1377 public List<Item[]> getYXOverlappingItemListLines() { 1282 1378 final List<XRawItem> yxOverlappingXRawItemList = getYXOverlappingXRawItemList(true); 1283 1379 final List<Item[]> lines = new ArrayList<Item[]>(); 1284 1380 final List<XRawItem> currentLineXItem = new ArrayList<XRawItem>(); 1285 for(final XRawItem xitem : yxOverlappingXRawItemList) { 1286 if(xitem.getItem() == GROUPSEP_START) continue; 1287 else if(xitem.getItem() == GROUPSEP_END) { 1288 if(currentLineXItem.isEmpty()) continue; 1381 for (final XRawItem xitem : yxOverlappingXRawItemList) { 1382 if (xitem.getItem() == GROUPSEP_START) 1383 continue; 1384 else if (xitem.getItem() == GROUPSEP_END) { 1385 if (currentLineXItem.isEmpty()) 1386 continue; 1289 1387 else { 1290 1388 final List<Item> ln = new ArrayList<Item>(); 1291 for(final XRawItem xrawitem : currentLineXItem) ln.add(xrawitem.getItem()); 1292 lines.add(ln.toArray(new Item[]{})); 1389 for (final XRawItem xrawitem : currentLineXItem) 1390 ln.add(xrawitem.getItem()); 1391 lines.add(ln.toArray(new Item[] {})); 1293 1392 currentLineXItem.clear(); 1294 1393 } 1295 1394 } else { 1296 if(currentLineXItem.isEmpty()) currentLineXItem.add(xitem); 1395 if (currentLineXItem.isEmpty()) 1396 currentLineXItem.add(xitem); 1297 1397 else { 1298 final YOverlappingItemsSpan[] itemSpanArray = xitem.getGroup().yitems_span_array; 1299 final int index = xitem.getBoundingYTop() - xitem.getGroup().getBoundingYTop(); 1398 final YOverlappingItemsSpan[] itemSpanArray = xitem 1399 .getGroup().yitems_span_array; 1400 final int index = xitem.getBoundingYTop() 1401 - xitem.getGroup().getBoundingYTop(); 1300 1402 final YOverlappingItemsSpan itemSpan = itemSpanArray[index]; 1301 final List<XItem> xOrderedList = itemSpan instanceof YOverlappingItemsTopEdge ? 1302 ((YOverlappingItemsTopEdge)itemSpan).getXOrderedLine().getXItemList() : 1303 ((YOverlappingItemsShadow)itemSpan).getTopEdge().getXOrderedLine().getXItemList(); 1304 if(!xOrderedList.contains(currentLineXItem.get(0))) { 1305 if(!currentLineXItem.isEmpty()) { 1403 final List<XItem> xOrderedList = itemSpan instanceof YOverlappingItemsTopEdge ? ((YOverlappingItemsTopEdge) itemSpan) 1404 .getXOrderedLine().getXItemList() 1405 : ((YOverlappingItemsShadow) itemSpan).getTopEdge() 1406 .getXOrderedLine().getXItemList(); 1407 if (!xOrderedList.contains(currentLineXItem.get(0))) { 1408 if (!currentLineXItem.isEmpty()) { 1306 1409 final List<Item> ln = new ArrayList<Item>(); 1307 for(final XRawItem xrawitem : currentLineXItem) ln.add(xrawitem.getItem()); 1308 lines.add(ln.toArray(new Item[]{})); 1410 for (final XRawItem xrawitem : currentLineXItem) 1411 ln.add(xrawitem.getItem()); 1412 lines.add(ln.toArray(new Item[] {})); 1309 1413 currentLineXItem.clear(); 1310 1414 } … … 1316 1420 } 1317 1421 } 1318 if (!currentLineXItem.isEmpty()) {1422 if (!currentLineXItem.isEmpty()) { 1319 1423 final List<Item> ln = new ArrayList<Item>(); 1320 for(final XRawItem xrawitem : currentLineXItem) ln.add(xrawitem.getItem()); 1321 lines.add(ln.toArray(new Item[]{})); 1424 for (final XRawItem xrawitem : currentLineXItem) 1425 ln.add(xrawitem.getItem()); 1426 lines.add(ln.toArray(new Item[] {})); 1322 1427 currentLineXItem.clear(); 1323 1428 } … … 1328 1433 final List<XRawItem> yxOverlappingXItemList = getYXOverlappingXRawItemList(separateGroups); 1329 1434 final ArrayList<Item> yxOverlappingItemList = new ArrayList<Item>(); 1330 for(final XRawItem xitem : yxOverlappingXItemList) yxOverlappingItemList.add(xitem.getItem()); 1435 for (final XRawItem xitem : yxOverlappingXItemList) 1436 yxOverlappingItemList.add(xitem.getItem()); 1331 1437 return yxOverlappingItemList; 1332 1438 } 1333 1334 public List<XRawItem> getYXOverlappingXRawItemList(final boolean separateGroups) { 1439 1440 public List<XRawItem> getYXOverlappingXRawItemList( 1441 final boolean separateGroups) { 1335 1442 ArrayList<XRawItem> overlapping_y_ordered_items = new ArrayList<XRawItem>(); 1336 1443 … … 1350 1457 if (xspan instanceof XRawItem) { 1351 1458 XRawItem xitem_span = (XRawItem) xspan; 1352 //Item item = xitem_span.getItem();1353 //1354 //overlapping_y_ordered_items.add(item);1459 // Item item = xitem_span.getItem(); 1460 // 1461 // overlapping_y_ordered_items.add(item); 1355 1462 overlapping_y_ordered_items.add(xitem_span); 1356 1463 } else { … … 1362 1469 .getYXOverlappingXRawItemList(separateGroups); 1363 1470 if (separateGroups) { 1364 overlapping_y_ordered_items.add(new XRawItem(GROUPSEP_START, this)); 1471 overlapping_y_ordered_items.add(new XRawItem( 1472 GROUPSEP_START, this)); 1365 1473 } 1366 1474 overlapping_y_ordered_items 1367 1475 .addAll(nested_overlapping_items); 1368 1476 if (separateGroups) { 1369 overlapping_y_ordered_items.add(new XRawItem(GROUPSEP_END, this)); 1477 overlapping_y_ordered_items.add(new XRawItem( 1478 GROUPSEP_END, this)); 1370 1479 } 1371 1480 } … … 1377 1486 return overlapping_y_ordered_items; 1378 1487 } 1379 1488 1380 1489 public ArrayList<Item> getYXOverlappingItemList() { 1381 1490 return getYXOverlappingItemList(false); 1382 1491 } 1383 1492 1384 1493 public String toString() { 1385 1494 return "XGroupItem"; 1386 1495 } 1387 1496 } 1388
Note:
See TracChangeset
for help on using the changeset viewer.