source: trunk/src/org/expeditee/io/flowlayout/XGroupItem.java@ 976

Last change on this file since 976 was 976, checked in by bln4, 8 years ago

When deciding what action to run Actions.java now considers actions with the same parameter list by emphasising some parameter types over others. Integer -> Double -> Float -> String -> All others

Now doesn't try to map in a item if the item for some reason has a null bounding_rect. A error message is displayed if it finds a null.

  • Property svn:executable set to *
File size: 43.7 KB
Line 
1/**
2 * XGroupItem.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.io.flowlayout;
20
21import java.awt.Point;
22import java.awt.Polygon;
23import java.awt.Rectangle;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.Comparator;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.LinkedList;
31import java.util.List;
32
33import org.expeditee.gui.Frame;
34import org.expeditee.gui.FrameUtils;
35import org.expeditee.items.Item;
36import org.expeditee.items.Line;
37import org.expeditee.items.Text;
38
39public 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
51 public static boolean doImplicitBoxing = true;
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
88 Frame frame;
89
90 protected int y_span_height;
91 protected YOverlappingItemsSpan[] yitems_span_array;
92
93 List<Text> raw_text_item_list;
94 List<XGroupItem> grouped_item_list;
95 List<Item> remaining_item_list;
96
97 FlowType out_of_flow;
98
99 protected XGroupItem() {
100 frame = null;
101
102 y_span_height = 0;
103 yitems_span_array = null;
104
105 raw_text_item_list = null;
106 grouped_item_list = null;
107 remaining_item_list = null;
108
109 out_of_flow = FlowType.in_flow;
110 }
111
112 public XGroupItem(Frame frame, List<Item> y_ordered_items,
113 Polygon enclosing_polygon) {
114 this.frame = frame;
115 this.out_of_flow = FlowType.in_flow;
116 ;
117
118 if (enclosing_polygon == null) {
119 // e.g. when the top-level case, with no enclosing polygon at this
120 // stage
121 // => generate one based on the bounding box of the y_ordered_items
122 enclosing_polygon = DimensionExtent
123 .boundingBoxPolygon(y_ordered_items);
124 }
125
126 Rectangle enclosing_bounding_rect = enclosing_polygon.getBounds();
127 initSpanArray(enclosing_bounding_rect);
128
129 // Step 1: Separate out the raw-text-items, the grouped-items and
130 // 'remaining' (e.g. points and lines)
131
132 raw_text_item_list = new ArrayList<Text>();
133 grouped_item_list = new ArrayList<XGroupItem>();
134 remaining_item_list = new ArrayList<Item>();
135
136 separateYOverlappingItems(frame, y_ordered_items, enclosing_polygon,
137 raw_text_item_list, grouped_item_list, remaining_item_list);
138
139 // Step 2: Add in the raw-text items
140 for (Text text_item : raw_text_item_list) {
141
142 XRawItem x_raw_item = new XRawItem(text_item, this);
143 // overspill can occur (and is acceptable) when raw-text item spills
144 // out of enclosing shape (such as a rectangle)
145 if(x_raw_item.bounding_rect == null) {
146 final StringBuilder errorMsg =
147 new StringBuilder("Was about to try mapInItem(XRawItem) but found a null bounding_rect. Item details: ");
148 final String nl = System.getProperty("line.separator");
149 errorMsg.append("\t Item parent: " + text_item.getParent() + nl);
150 errorMsg.append("\t Item position: " + text_item.getPosition() + nl);
151 errorMsg.append("\t Item text content: " + text_item.getText() + nl);
152 System.err.println(errorMsg.toString());
153 }
154 else mapInItem(x_raw_item);
155 }
156
157 }
158
159 public XGroupItem(Frame frame, List<Item> y_ordered_items) {
160 this(frame, y_ordered_items, null);
161 }
162
163 protected XGroupItem(XGroupItem imprint, Rectangle copy_to_bounding_rect) {
164 super();
165
166 // Implement a shallow copy => share references to frame, and item list,
167 // and y-span
168 // Only the bounding box is changed => set to the
169 // 'copy_to_bounding_rect' passed in
170
171 this.frame = imprint.frame;
172
173 y_span_height = imprint.y_span_height;
174 yitems_span_array = imprint.yitems_span_array;
175
176 this.raw_text_item_list = imprint.raw_text_item_list;
177 this.grouped_item_list = imprint.grouped_item_list;
178
179 // int offX = imprint.getBoundingRect().x - copy_to_bounding_rect.x;
180 // int offY = imprint.getBoundingRect().y - copy_to_bounding_rect.y;
181 // this.grouped_item_list = new ArrayList<XGroupItem>();
182 // for(XGroupItem newChild : imprint.grouped_item_list) {
183 // Rectangle newRect = newChild.getBoundingRect();
184 // newRect.x -= offX;
185 // newRect.y -= offY;
186 // this.grouped_item_list.add(new XGroupItem(newChild, newRect));
187 // }
188 this.remaining_item_list = imprint.remaining_item_list;
189
190 this.out_of_flow = imprint.out_of_flow; // Or perhaps set it to
191 // FlowType.out_of_flow_fake_position
192 // straight away?
193
194 this.bounding_rect = new Rectangle(copy_to_bounding_rect); // deep copy
195 // to be on
196 // the safe
197 // side
198 }
199
200 protected void initSpanArray(Rectangle bounding_rect) {
201 this.bounding_rect = bounding_rect;
202
203 int y_top = getBoundingYTop();
204 int y_bot = getBoundingYBot();
205
206 if (y_top <= y_bot) {
207 // => have non-trivial span
208
209 y_span_height = (y_bot - y_top) + 2; // Polygon in Java excludes
210 // right and bottom sides so
211 // need extra "+1" in calc
212
213 yitems_span_array = new YOverlappingItemsSpan[y_span_height];
214 } else {
215 y_span_height = 0;
216 yitems_span_array = null;
217 }
218 }
219
220 public YOverlappingItemsSpan getSpanItemAt(int index) {
221 return yitems_span_array[index];
222 }
223
224 public void setOutOfFlow(FlowType flow_type) {
225 out_of_flow = flow_type;
226 }
227
228 public FlowType getFlowItem() {
229 return out_of_flow;
230 }
231
232 public boolean isOriginalOutOfFlowItem() {
233 return out_of_flow == FlowType.out_of_flow_original;
234 }
235
236 public boolean isFakedOutOfFlowItem() {
237 return out_of_flow == FlowType.out_of_flow_faked_position;
238 }
239
240 public List<Text> getRawTextItemList() {
241 return raw_text_item_list;
242 }
243
244 public List<Text[]> getRawTextLines() {
245 final List<Text> rawText = getRawTextItemList();
246 final List<Text> lineStarts = new LinkedList<Text>();
247 for (final YOverlappingItemsSpan span : this.yitems_span_array) {
248 if (span instanceof YOverlappingItemsTopEdge) {
249 final YOverlappingItemsTopEdge topEdge = (YOverlappingItemsTopEdge) span;
250 final List<XItem> xLine = topEdge.getXOrderedLine()
251 .getXItemList();
252 for (final XItem xitem : xLine) {
253 if (xitem instanceof XRawItem) {
254 final Text item = (Text) ((XRawItem) xitem).getItem();
255 lineStarts.add(item);
256 break;
257 }
258 }
259 }
260 }
261 final List<Integer> indexes = new LinkedList<Integer>();
262 for (final Text lineStart : lineStarts)
263 indexes.add(rawText.indexOf(lineStart));
264 final List<Text[]> ret = new LinkedList<Text[]>();
265 int rangeEnd = indexes.get(0);
266 int last = rangeEnd;
267 for (int i = 1; i < indexes.size(); i++) {
268 rangeEnd = indexes.get(i);
269 final List<Text> part = new LinkedList<Text>(rawText.subList(0,
270 rangeEnd - last));
271 last = rangeEnd;
272 rawText.removeAll(part);
273 part.toArray(new Text[] {});
274 ret.add(part.toArray(new Text[] {}));
275 }
276 ret.add(rawText.toArray(new Text[] {}));
277 return ret;
278 }
279
280 public List<XGroupItem> getGroupedItemList() {
281 return grouped_item_list;
282 }
283
284 public List<Item> getRemainingItemList() {
285 return remaining_item_list;
286 }
287
288 public int getItemSpanLength() {
289 return yitems_span_array.length;
290 }
291
292 public YOverlappingItemsSpan getYOverlappingItemsSpan(int y) {
293 int y_index = y - getBoundingYTop();
294
295 if ((y_index < 0) || (y_index >= yitems_span_array.length)) {
296 int y_top = getBoundingYTop();
297 int y_bot = y_top + yitems_span_array.length - 1;
298
299 System.err
300 .println("Error in getYOverlappingItemsSpan(): index out of bounds for value "
301 + y);
302 System.err.println(" => Operation mapped into local array: y-top="
303 + y_top + ", y-bot=" + y_bot);
304
305 return null;
306 }
307 return yitems_span_array[y_index];
308 }
309
310 protected int cropToTop(int y) {
311 int y_index = y - getBoundingYTop();
312
313 if (y_index < 0) {
314 y = getBoundingYTop();
315 }
316
317 return y;
318 }
319
320 protected int cropToBot(int y) {
321 int y_index = y - getBoundingYTop();
322
323 if (y_index >= yitems_span_array.length) {
324 y = getBoundingYBot();
325 }
326
327 return y;
328 }
329
330 public void setYOverlappingItemsSpan(int y,
331 YOverlappingItemsSpan yitems_span) {
332 int y_index = y - getBoundingYTop();
333
334 if ((y_index < 0) || (y_index >= yitems_span_array.length)) {
335
336 int y_top = getBoundingYTop();
337 int y_bot = y_top + yitems_span_array.length - 1;
338
339 System.err
340 .println("Error in setYOverlappingItemsSpan(): index out of bounds for value "
341 + y);
342 System.err.println(" => Operation mapped into local array: y-top="
343 + y_top + ", y-bot=" + y_bot);
344
345 return;
346 }
347 yitems_span_array[y_index] = yitems_span;
348 }
349
350 protected List<Item> followLinesToArrowHeads(Collection<Item> visited,
351 Item anchor_item, List<Line> used_in_lines) {
352 List<Item> arrow_head_endpoints = new ArrayList<Item>();
353
354 for (Line line : used_in_lines) {
355
356 Item start_item = line.getStartItem();
357
358 if (start_item == anchor_item) {
359 // the line we're considering is heading in the right direction
360
361 Item end_item = line.getEndItem();
362
363 if (!visited.contains(end_item)) {
364 // Needs processing
365 visited.add(end_item);
366
367 List<Line> follow_lines = end_item.getLines();
368
369 if (follow_lines.size() == 1) {
370 // reached an end-point
371 if (end_item.hasVisibleArrow()) {
372 arrow_head_endpoints.add(end_item);
373 }
374 } else if (follow_lines.size() > 1) {
375
376 List<Item> followed_arrow_heads = followLinesToArrowHeads(
377 visited, end_item, follow_lines);
378 arrow_head_endpoints.addAll(followed_arrow_heads);
379 }
380 }
381 }
382
383 }
384 return arrow_head_endpoints;
385 }
386
387 protected XGroupItem innermostXGroup(Item arrow_head,
388 List<XGroupItem> grouped_item_list) {
389 XGroupItem innermost_item = null;
390
391 for (XGroupItem xgroup_item : grouped_item_list) {
392 if (xgroup_item.containsItem(arrow_head)) {
393
394 innermost_item = xgroup_item;
395
396 // Now see if it is in any of the nested ones?
397
398 List<XGroupItem> nested_group_item_list = xgroup_item.grouped_item_list;
399
400 XGroupItem potentially_better_item = innermostXGroup(
401 arrow_head, nested_group_item_list);
402
403 if (potentially_better_item != null) {
404 innermost_item = potentially_better_item;
405 }
406 break;
407 }
408
409 }
410
411 return innermost_item;
412 }
413
414 protected void removeXGroupItem(XGroupItem remove_item,
415 List<XGroupItem> grouped_item_list) {
416
417 for (XGroupItem xgroup_item : grouped_item_list) {
418
419 if (xgroup_item == remove_item) {
420 grouped_item_list.remove(xgroup_item);
421 return;
422 } else {
423 List<XGroupItem> nested_group_item_list = xgroup_item.grouped_item_list;
424 removeXGroupItem(remove_item, nested_group_item_list);
425
426 }
427 }
428 }
429
430 protected boolean daisyChainContainsLoop(final XGroupItem toplevel_xgroup,
431 final Item exitingDot, Collection<XGroupItem> groupsSeen) {
432 final Item start_item = exitingDot;
433 final Item end_item = start_item.getLines().get(0).getEndItem();
434 final XGroupItem xgroup = innermostXGroup(end_item,
435 toplevel_xgroup.getGroupedItemList());
436
437 if(xgroup == null) return false;
438
439 if (groupsSeen.contains(xgroup))
440 return true;
441
442 groupsSeen.add(xgroup);
443
444 for (final Item i : xgroup.remaining_item_list) {
445 final List<Line> lines = i.getLines();
446 if (lines.size() == 1 && lines.get(0).getStartItem() == i) {
447 return daisyChainContainsLoop(toplevel_xgroup, i, groupsSeen);
448 }
449 }
450 return false;
451 }
452
453 protected void repositionOutOfFlowGroupsFollowLine(
454 XGroupItem toplevel_xgroup, Item remaining_item,
455 Collection<XGroupItem> out_of_flow) {
456 // See if this item is the start of a line
457 // Follow it if it is
458 // For each end of the line (potentially a multi-split poly-line) that
459 // has an arrow head:
460 // See if the arrow-head falls inside an XGroup area
461 // => Ignore endings that are in the same XGroupItem as the line started
462 // in
463 List<Line> used_in_lines = remaining_item.getLines();
464 if (used_in_lines.size() == 1) {
465 // at the start (or end) of a line
466
467 Item start_item = used_in_lines.get(0).getStartItem();
468
469 if (remaining_item == start_item) {
470
471 // found the start of a line
472
473 Collection<Item> visited = new HashSet<Item>();
474 visited.add(remaining_item);
475
476 List<Item> arrow_head_endpoints = followLinesToArrowHeads(
477 visited, remaining_item, used_in_lines);
478
479 // System.out.println("**** For Xgroup " + this +
480 // " with dot starting at " + remaining_item + " has " +
481 // arrow_head_endpoints.size() + " arrow head endpoints");
482
483 Collections.sort(arrow_head_endpoints,
484 new MultiArrowHeadComparable());
485
486 for (Item arrow_head : arrow_head_endpoints) {
487 // find the inner-most group it falls within
488
489 List<XGroupItem> toplevel_grouped_item_list = toplevel_xgroup
490 .getGroupedItemList();
491
492 XGroupItem xgroup_item = innermostXGroup(arrow_head,
493 toplevel_grouped_item_list);
494
495 if (xgroup_item != null) {
496
497 // Ignore if the found 'xgroup_item' is 'this'
498 // (i.e. the found arrow head is at the same XGroupItem
499 // level we are currently processing)
500
501 if (xgroup_item != this) {
502
503 if (/* out_of_flow.contains(xgroup_item) && */daisyChainContainsLoop(
504 toplevel_xgroup, start_item,
505 new ArrayList<XGroupItem>())) {
506 System.err.println("#Found infinite loop; not following. On frame: " +
507 start_item.getParent().getName() + " At position: " +
508 start_item.getPosition());
509 continue;
510 }
511
512 if (!out_of_flow.contains(xgroup_item)) {
513 for (Item remaining_item_deeper : xgroup_item
514 .getRemainingItemList()) {
515 xgroup_item
516 .repositionOutOfFlowGroupsFollowLine(
517 toplevel_xgroup,
518 remaining_item_deeper,
519 out_of_flow);
520 }
521 }
522
523 out_of_flow.add(xgroup_item);
524
525 // Can't delete here, as it causes a concurrent
526 // exception => add to 'out_of_flow' hashmap and
527 // perform removal later
528
529 // System.out.println("**** innermost XGroupItem = "
530 // + xgroup_item);
531
532 // Artificially rework its (x,y) org and dimension
533 // to make it appear where the start of the arrow is
534
535 Rectangle start_rect = start_item.getArea()
536 .getBounds();
537
538 XGroupItem xgroup_item_shallow_copy = new XGroupItem(
539 xgroup_item, start_rect);
540
541 // Perhaps the following two lines should be moved
542 // to inside the constructor??
543
544 xgroup_item
545 .setOutOfFlow(FlowType.out_of_flow_original);
546 xgroup_item_shallow_copy
547 .setOutOfFlow(FlowType.out_of_flow_faked_position);
548
549 // xgroup_item.setBoundingRect(start_rect);
550 // xgroup_item.setOutOfFlow();
551
552 // Finally add it in
553 // mapInItem(xgroup_item);
554 mapInItem(xgroup_item_shallow_copy);
555 }
556 }
557 }
558 }
559
560 }
561 }
562
563 /**
564 * Look for any 'out-of-flow' XGroup boxes, signalled by the user drawing an
565 * arrow line to it => force an artificial change to such boxes so its
566 * dimensions becomes the starting point of the arrow line. This will make
567 * it sort to the desired spot in the YOverlapping span
568 */
569
570 public void repositionOutOfFlowGroupsRecursive(XGroupItem toplevel_xgroup,
571 Collection<XGroupItem> out_of_flow) {
572 // Map in all the items in the given list:
573 for (Item remaining_item : remaining_item_list) {
574
575 repositionOutOfFlowGroupsFollowLine(toplevel_xgroup,
576 remaining_item, out_of_flow);
577 }
578
579 // Now recursively work through each item's nested x-groups
580 for (XGroupItem xgroup_item : grouped_item_list) {
581
582 if (!out_of_flow.contains(xgroup_item)) {
583 xgroup_item.repositionOutOfFlowGroupsRecursive(toplevel_xgroup,
584 out_of_flow);
585 }
586 }
587 }
588
589 public void repositionOutOfFlowGroups(XGroupItem toplevel_xgroup) {
590 // Collection<XGroupItem> out_of_flow = new HashSet<XGroupItem>();
591 Collection<XGroupItem> out_of_flow = new ArrayList<XGroupItem>();
592
593 repositionOutOfFlowGroupsRecursive(toplevel_xgroup, out_of_flow);
594
595 // List<XGroupItem >toplevel_grouped_item_list =
596 // toplevel_xgroup.getGroupedItemList();
597
598 // ****
599
600 // Want to remove the original "out-of-position" blocks that were found,
601 // as (for each arrow
602 // point to such an original) shallow-copies now exist with 'faked'
603 // positions that correspond
604 // to where the arrows start.
605
606 // No longer remove them from the nested grouped structure, rather, rely
607 // on these items being
608 // tagged "isOutOfFLow()" to be skipped by subsequent traversal calls
609 // (e.g., to generate
610 // the normal "in-flow" nested blocks)
611
612 // Structuring things this way, means that it is now possible to have
613 // multiple arrows
614 // pointing to the same block of code, and both "out-of-flow" arrows
615 // will be honoured,
616 // replicating the code at their respective start points
617
618 /*
619 * for (XGroupItem xgroup_item: out_of_flow) {
620 * removeXGroupItem(xgroup_item,toplevel_grouped_item_list); }
621 */
622
623 }
624
625 /**
626 * Focusing only on enclosures that fit within the given polygon *and* have
627 * this item in it, the method finds the largest of these enclosures and
628 * returns all the items it contains
629 *
630 * @return
631 */
632 public Collection<Item> getItemsInNestedEnclosure(Item given_item,
633 AreaPolygon outer_polygon) {
634 Collection<Item> sameEnclosure = null;
635 Collection<Item> seen = new HashSet<Item>();
636
637 double enclosureArea = 0.0;
638
639 for (Item i : frame.getItems()) {
640
641 // Go through all the enclosures ...
642
643 if (!seen.contains(i) && i.isEnclosed()) {
644
645 Collection<Item> i_enclosing_dots_coll = i.getEnclosingDots();
646 List<Item> i_enclosing_dots = new ArrayList<Item>(
647 i_enclosing_dots_coll); // Change to List of items
648
649 seen.addAll(i_enclosing_dots);
650
651 Polygon i_polygon = new Polygon();
652 for (int di = 0; di < i_enclosing_dots.size(); di++) {
653 Item d = i_enclosing_dots.get(di);
654
655 i_polygon.addPoint(d.getX(), d.getY());
656 }
657
658 // ... looking for one that is completely contained in the
659 // 'outer_polygon' and ...
660 if (outer_polygon.completelyContains(i_polygon)) {
661
662 Collection<Item> enclosed = i.getEnclosedItems();
663
664 // ... includes this item
665 if (enclosed.contains(given_item)) {
666
667 // Remember it if it is larger than anything we've
668 // encountered before
669 if ((i.getEnclosedArea() > enclosureArea)) {
670 enclosureArea = i.getEnclosedArea();
671 sameEnclosure = enclosed;
672 }
673 }
674 }
675
676 }
677 }
678
679 if (sameEnclosure == null)
680 return new LinkedList<Item>();
681
682 return sameEnclosure;
683 }
684
685 public void separateYOverlappingItems(Frame frame, List<Item> item_list,
686 Polygon enclosing_polygon, List<Text> raw_text_item_list,
687 List<XGroupItem> grouped_item_list, List<Item> remaining_item_list) {
688 final List<Item> origonal_item_list = new ArrayList<Item>(item_list);
689 // raw_text_items_list => all the non-enclosed text items
690 // grouped_items_list => list of all enclosures containing text items
691 // remaining_item_list => whatever is left: mostly dots that form part
692 // of lines/polylines/polygons
693
694 AreaPolygon area_enclosing_polygon = new AreaPolygon(enclosing_polygon);
695
696 List<AreaPolygon> area_enclosed_polygon_list = new ArrayList<AreaPolygon>();
697
698 while (item_list.size() > 0) {
699 Item item = item_list.remove(0); // shift
700
701 if (item instanceof Text) {
702 Text text = (Text) item;
703
704 Collection<Item> items_in_nested_enclosure_coll = getItemsInNestedEnclosure(
705 text, area_enclosing_polygon);
706 List<Item> items_in_nested_enclosure = new ArrayList<Item>(
707 items_in_nested_enclosure_coll); // Change to handling
708 // it as a list
709
710 if (items_in_nested_enclosure.size() == 0) {
711 // if(text.getPixelBoundsUnion().x >=
712 // this.getBoundingXLeft() && text.getPixelBoundsUnion().y
713 // >= this.getBoundingYTop())
714 // raw_text_item_list.add(text);
715 // else remaining_item_list.add(text);
716 raw_text_item_list.add(text);
717 } else {
718
719 // Something other than just this text-item is around
720
721 while (items_in_nested_enclosure.size() > 0) {
722
723 Item enclosure_item = items_in_nested_enclosure
724 .remove(0); // shift
725
726 Polygon enclosed_polygon = enclosure_item
727 .getEnclosedShape();
728
729 if (enclosed_polygon != null) {
730 // Got a group
731 // => Remove any items-in-nested-enclosure from:
732 //
733 // 'item_list' (so we don't waste our time
734 // discovering and processing again these related
735 // items)
736 // and
737 // 'raw_text_item_list' and 'remaining_item_lst' (as
738 // we now know they are part of the nested enclosed
739 // group)
740
741 AreaPolygon area_enclosed_polygon = new AreaPolygon(
742 enclosed_polygon);
743 area_enclosed_polygon_list
744 .add(area_enclosed_polygon);
745
746 item_list.remove(enclosure_item);
747 item_list.removeAll(items_in_nested_enclosure);
748
749 raw_text_item_list.remove(enclosure_item);
750 raw_text_item_list
751 .removeAll(items_in_nested_enclosure);
752
753 remaining_item_list.remove(enclosure_item);
754 remaining_item_list
755 .removeAll(items_in_nested_enclosure);
756
757 // Now remove any of the just used enclosed-items if
758 // they formed part of the perimeter
759 List<Item> items_on_perimeter = new ArrayList<Item>();
760
761 Iterator<Item> item_iterator = items_in_nested_enclosure
762 .iterator();
763 while (item_iterator.hasNext()) {
764 Item item_to_check = item_iterator.next();
765 Point pt_to_check = new Point(
766 item_to_check.getX(),
767 item_to_check.getY());
768
769 if (area_enclosed_polygon
770 .isPerimeterPoint(pt_to_check)) {
771 items_on_perimeter.add(item_to_check);
772 }
773 }
774
775 items_in_nested_enclosure
776 .removeAll(items_on_perimeter);
777 }
778
779 else {
780 // Text or single point (with no enclosure)
781
782 // This item doesn't feature at this level
783 // => will be subsequently capture by the group
784 // polygon below
785 // and processed by the recursive call
786
787 item_list.remove(enclosure_item);
788 }
789 }
790
791 }
792
793 } // end of Text test
794 else {
795 // non-text item => add to remaining_item_list
796 remaining_item_list.add(item);
797 }
798
799 }
800
801 // Sort areas, smallest to largest
802 Collections.sort(area_enclosed_polygon_list,
803 new Comparator<AreaPolygon>() {
804
805 public int compare(AreaPolygon ap1, AreaPolygon ap2) {
806 Double ap1_area = ap1.getArea();
807 Double ap2_area = ap2.getArea();
808
809 return ap2_area.compareTo(ap1_area);
810 }
811 });
812
813 // Remove any enclosed polygon areas that are completely contained in a
814 // larger one
815
816 for (int ri = 0; ri < area_enclosed_polygon_list.size(); ri++) {
817 // ri = remove index pos
818
819 AreaPolygon rpoly = area_enclosed_polygon_list.get(ri);
820
821 for (int ci = ri + 1; ci < area_enclosed_polygon_list.size(); ci++) {
822 // ci = check index pos
823 AreaPolygon cpoly = area_enclosed_polygon_list.get(ci);
824 if (rpoly.completelyContains(cpoly)) {
825 area_enclosed_polygon_list.remove(ci);
826 ri--; // to offset to the outside loop increment
827 break;
828 }
829 }
830 }
831
832 // By this point, guaranteed that the remaining polygons are the largest
833 // ones
834 // that capture groups of items
835 //
836 // There may be sub-groupings within them, which is the reason for the
837 // recursive call below
838
839 for (AreaPolygon area_polygon : area_enclosed_polygon_list) {
840
841 Collection<Item> enclosed_items = FrameUtils.getItemsEnclosedBy(
842 frame, area_polygon);
843 List<Item> enclosed_item_list = new ArrayList<Item>(enclosed_items);
844
845 int i = 0;
846 while (i < enclosed_item_list.size()) {
847 Item enclosed_item = enclosed_item_list.get(i);
848
849 // Filter out enclosed-items points that are part of the
850 // polygon's perimeter
851 if (area_polygon.isPerimeterPoint(enclosed_item.getPosition())) {
852 enclosed_item_list.remove(i);
853 // Don't include items the user hasn't asked us to.
854 }
855 // Only include items the user has asked to to include.
856 else if (!origonal_item_list.contains(enclosed_item))
857 enclosed_item_list.remove(i);
858 else {
859 i++;
860 }
861 }
862
863 // Recursively work on the identified sub-group
864
865 XGroupItem xgroup_item = new XGroupItem(frame, enclosed_item_list,
866 area_polygon);
867
868 grouped_item_list.add(xgroup_item);
869 }
870 }
871
872 protected void castShadowIntoEmptySpace(
873 YOverlappingItemsTopEdge y_item_span_top_edge, int yt, int yb) {
874 // Assumes that all entries cast into are currently null
875 // => Use the more general castShadow() below, if this is not guaranteed
876 // to be the case
877
878 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(
879 y_item_span_top_edge);
880
881 for (int y = yt; y <= yb; y++) {
882
883 setYOverlappingItemsSpan(y, y_span_shadow);
884 }
885 }
886
887 protected void castShadow(YOverlappingItemsTopEdge y_item_span_top_edge,
888 int yt, int yb) {
889 // Cast shadows only in places where there are currently no shadow
890 // entries
891
892 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(
893 y_item_span_top_edge);
894
895 int y = yt;
896
897 while (y <= yb) {
898 YOverlappingItemsSpan y_item_span = getYOverlappingItemsSpan(y);
899
900 if (y_item_span == null) {
901 setYOverlappingItemsSpan(y, y_span_shadow);
902 y++;
903 } else if (y_item_span instanceof YOverlappingItemsTopEdge) {
904 // Our shadow has run into another top-edged zone
905 // => need to extend the shadow to include this zone as well
906 // (making this encountered top-edge obsolete)
907
908 // Merge the newly encountered top-line in with the current one
909 // and change all its shadow references to our (dominant) one
910
911 XOrderedLine dominant_x_line = y_item_span_top_edge
912 .getXOrderedLine();
913
914 YOverlappingItemsTopEdge obsolete_top_edge = (YOverlappingItemsTopEdge) y_item_span;
915 XOrderedLine obsolete_x_line = obsolete_top_edge
916 .getXOrderedLine();
917
918 for (XItem xitem : obsolete_x_line.getXItemList()) {
919
920 dominant_x_line.orderedMergeItem(xitem);
921 }
922
923 while (y_item_span != null) {
924
925 setYOverlappingItemsSpan(y, y_span_shadow);
926
927 y++;
928
929 if (y <= yb) {
930 y_item_span = getYOverlappingItemsSpan(y);
931 } else {
932 y_item_span = null;
933 }
934 }
935 } else {
936 y++;
937 }
938 }
939 }
940
941 protected int autocropToTop(XItem xitem) {
942 // top-edge over-spill can occur (and is acceptable) when (for example)
943 // the given xitem is a raw-text item
944 // that doesn't neatly fit in an enclosed area (i,e., is poking out the
945 // top of an enclosing rectangle)
946 // => cut down to the top of 'this' xgroupitem
947
948 int yt = xitem.getBoundingYTop();
949
950 yt = cropToTop(yt); // Only changes yt if in an over-spill situation
951
952 return yt;
953
954 }
955
956 protected int autocropToBot(XItem xitem) {
957
958 // similar to autocropToTop(), bottom-edge over-spill can occur (and is
959 // acceptable) with
960 // a less than precise placed raw-text item
961
962 int yb = xitem.getBoundingYBot();
963
964 yb = cropToBot(yb); // Only changes yb if in an over-spill situation
965
966 return yb;
967 }
968
969 protected boolean yExtentIntersection(XGroupItem xgroup_item1,
970 XGroupItem xgroup_item2) {
971 // Computation applied to y-extent of each rectangle
972
973 // To intersect, either a point in item1 falls inside item2,
974 // or else item2 is completely within item1
975
976 int yt1 = xgroup_item1.getBoundingYTop();
977 int yb1 = xgroup_item1.getBoundingYBot();
978
979 int yt2 = xgroup_item2.getBoundingYTop();
980
981 // yt1 insides item2?
982 if (xgroup_item2.containedInYExtent(yt1)) {
983 return true;
984 }
985
986 // yb1 inside item2?
987 if (xgroup_item2.containedInYExtent(yb1)) {
988 return true;
989 }
990
991 // item2 completely inside item1?
992 //
993 // With the previous testing, this can be determined
994 // by checking only one of the values is inside.
995 // Doesn't matter which => choose yt2
996
997 if (xgroup_item1.containedInYExtent(yt2)) {
998 return true;
999 }
1000
1001 // Get to here if there is no intersection
1002 return false;
1003
1004 }
1005
1006 protected ArrayList<XGroupItem> calcXGroupYExtentIntersection(
1007 XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) {
1008 ArrayList<XGroupItem> intersect_list = new ArrayList<XGroupItem>();
1009
1010 for (XGroupItem xgroup_item : xgroup_item_list) {
1011
1012 if (xgroup_item == xgroup_pivot_item) {
1013 // Don't bother working out the intersection with itself!
1014 continue;
1015 }
1016
1017 if (yExtentIntersection(xgroup_pivot_item, xgroup_item)) {
1018 intersect_list.add(xgroup_item);
1019 }
1020 }
1021
1022 return intersect_list;
1023 }
1024
1025 protected ArrayList<YOverlappingItemsTopEdge> calcYSpanOverlap(XItem xitem) {
1026 int yt = autocropToTop(xitem);
1027 int yb = autocropToBot(xitem);
1028
1029 ArrayList<YOverlappingItemsTopEdge> top_edge_list = new ArrayList<YOverlappingItemsTopEdge>();
1030
1031 for (int y = yt; y <= yb; y++) {
1032
1033 YOverlappingItemsSpan item_span = getYOverlappingItemsSpan(y);
1034
1035 if (item_span != null) {
1036
1037 if (item_span instanceof YOverlappingItemsTopEdge) {
1038
1039 YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span;
1040
1041 top_edge_list.add(y_item_span_top_edge);
1042 }
1043 }
1044
1045 }
1046
1047 return top_edge_list;
1048 }
1049
1050 protected boolean multipleYSpanOverlap(XItem xitem) {
1051 return calcYSpanOverlap(xitem).size() > 0;
1052 }
1053
1054 public void mapInItem(XItem xitem) {
1055
1056 int yt = autocropToTop(xitem);
1057 int yb = autocropToBot(xitem);
1058 /*
1059 * int yt = xitem.getBoundingYTop(); int yb = xitem.getBoundingYBot();
1060 *
1061 * // top-edge over-spill can occur (and is acceptable) when (for
1062 * example) the given xitem is a raw-text item // that doesn't neatly
1063 * fit in an enclosed area (i,e., is poking out the top of an enclosing
1064 * rectangle) yt = cropToTop(yt); // Only changes yt if in an over-spill
1065 * situation
1066 *
1067 * // similarly, bottom-edge over-spill can occur (and is acceptable)
1068 * with a less than precise placed raw-text item yb = cropToBot(yb); //
1069 * Only changes yb if in an over-spill situation
1070 */
1071
1072 // Merge into 'items_span' checking for overlaps (based on xitem's
1073 // y-span)
1074
1075 boolean merged_item = false;
1076 for (int y = yt; y <= yb; y++) {
1077
1078 YOverlappingItemsSpan item_span = getYOverlappingItemsSpan(y);
1079
1080 if (item_span != null) {
1081
1082 if (item_span instanceof YOverlappingItemsTopEdge) {
1083
1084 // Hit a top edge of an existing item
1085
1086 // Need to:
1087 // 1. *Required* Insert new item into current x-ordered line
1088 // 2. *Conditionally* Move entire x-ordered line up to
1089 // higher 'y' top edge
1090 // 3. *Required* Cast shadow for new item
1091
1092 // Note for Step 2:
1093 // i) No need to do the move if y == yt
1094 // (the case when the new top edge is exactly the same
1095 // height as the existing one)
1096 // ii) If moving top-edge (the case when y > yt) then no
1097 // need to recalculate the top edge for existing shadows, as
1098 // this 'connection' is stored as a reference
1099
1100 // Step 1: insert into existing top-edge
1101
1102 YOverlappingItemsTopEdge y_item_span_top_edge = (YOverlappingItemsTopEdge) item_span;
1103 XOrderedLine xitem_span = y_item_span_top_edge
1104 .getXOrderedLine();
1105
1106 xitem_span
1107 .orderedMergeItem(xitem.getBoundingXLeft(), xitem);
1108
1109 // Step 2: if our new top-edge is higher than the existing
1110 // one, then need to move existing top-edge
1111 if (y > yt) {
1112
1113 // Move to new position
1114 setYOverlappingItemsSpan(yt, y_item_span_top_edge);
1115
1116 // Old position needs to become a shadow reference
1117 YOverlappingItemsShadow y_span_shadow = new YOverlappingItemsShadow(
1118 y_item_span_top_edge);
1119 setYOverlappingItemsSpan(y, y_span_shadow);
1120 }
1121
1122 // Step 3: Cast shadow
1123 castShadow(y_item_span_top_edge, yt + 1, yb);
1124
1125 } else {
1126 // Top edge to our new item has hit a shadow entry (straight
1127 // off)
1128 // => Look up what the shadow references, and then add in to
1129 // that
1130
1131 // Effectively after the shadow reference lookup this is the
1132 // same
1133 // as the above, without the need to worry about Step 2 (as
1134 // no move is needed)
1135
1136 YOverlappingItemsShadow y_item_span_shadow = (YOverlappingItemsShadow) item_span;
1137 YOverlappingItemsTopEdge y_item_span_top_edge = y_item_span_shadow
1138 .getTopEdge();
1139
1140 XOrderedLine xitem_span = y_item_span_top_edge
1141 .getXOrderedLine();
1142
1143 // merge with this item list, preserving x ordering
1144 xitem_span
1145 .orderedMergeItem(xitem.getBoundingXLeft(), xitem);
1146
1147 // Now Cast shadow
1148 castShadow(y_item_span_top_edge, yt + 1, yb);
1149 }
1150
1151 merged_item = true;
1152 break;
1153
1154 }
1155
1156 }
1157
1158 // Having checked all the y-location's ('yt' to 'yb') of this x-item, if
1159 // all y-span entries were found to be null
1160 // => 'merged_item' is still false
1161
1162 if (!merged_item) {
1163 // xitem didn't intersect with any existing y-spans
1164 // => simple case for add (i.e. all entries affected by map are
1165 // currently null)
1166
1167 // Start up a new x-ordered-line (consisting of only 'xitem'), add
1168 // in top edge and cast shadow
1169
1170 XOrderedLine xitem_line = new XOrderedLine(xitem);
1171
1172 YOverlappingItemsTopEdge y_item_span_top_edge = new YOverlappingItemsTopEdge(
1173 xitem_line);
1174
1175 setYOverlappingItemsSpan(yt, y_item_span_top_edge);
1176
1177 castShadowIntoEmptySpace(y_item_span_top_edge, yt + 1, yb);
1178 }
1179
1180 }
1181
1182 private ArrayList<DimensionExtent> calcXGroupXGaps(
1183 XGroupItem xgroup_pivot_item, List<XGroupItem> xgroup_item_list) {
1184 // 'xgroup_pivot_item' current not used!!
1185
1186 int enclosing_xl = getBoundingXLeft();
1187 int enclosing_xr = getBoundingXRight();
1188
1189 int enclosing_x_dim = enclosing_xr - enclosing_xl + 1;
1190 boolean is_x_shadow[] = new boolean[enclosing_x_dim]; // defaults all
1191 // values to
1192 // false
1193
1194 ArrayList<DimensionExtent> x_gap_list = new ArrayList<DimensionExtent>();
1195
1196 for (XGroupItem xgroup_item : xgroup_item_list) {
1197 int xl = xgroup_item.getBoundingXLeft();
1198 int xr = xgroup_item.getBoundingXRight();
1199
1200 for (int x = xl; x <= xr; x++) {
1201 int x_offset = x - enclosing_xl;
1202 is_x_shadow[x_offset] = true;
1203 }
1204 }
1205
1206 // New find where the contiguous runs of 'false' are, as they point to
1207 // the gaps
1208
1209 int x = 1;
1210 int start_x_gap = enclosing_xl;
1211 boolean in_gap = true;
1212
1213 while (x < is_x_shadow.length) {
1214 boolean prev_is_shadow = is_x_shadow[x - 1];
1215 boolean this_is_shadow = is_x_shadow[x];
1216
1217 if (prev_is_shadow && !this_is_shadow) {
1218 // reached end of shadow, record start of a gap
1219 start_x_gap = enclosing_xl + x;
1220 in_gap = true;
1221 } else if (!prev_is_shadow && this_is_shadow) {
1222 // reached the end of a gap, record it
1223 int end_x_gap = enclosing_xl + x - 1;
1224 DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap);
1225 x_gap_list.add(de);
1226 in_gap = false;
1227 }
1228 x++;
1229 }
1230
1231 if (in_gap) {
1232 int end_x_gap = enclosing_xl + x - 1;
1233 DimensionExtent de = new DimensionExtent(start_x_gap, end_x_gap);
1234 x_gap_list.add(de);
1235 }
1236
1237 return x_gap_list;
1238 }
1239
1240 protected void boxMultipleXRawItemsInGaps(XGroupItem xgroup_pivot_item,
1241 ArrayList<DimensionExtent> x_text_gap_list) {
1242 ArrayList<YOverlappingItemsTopEdge> y_span_overlap = calcYSpanOverlap(xgroup_pivot_item);
1243
1244 // work out where continuous runs of raw-text items intersect with the
1245 // gaps occurring between boxed items
1246
1247 // foreach x-gap,
1248 // foreach y-span's list of x-sorted objects,
1249 // => see how many raw-text items fit into it
1250
1251 ArrayList<ArrayList<XRawItem>> implicit_box_list = new ArrayList<ArrayList<XRawItem>>();
1252
1253 for (DimensionExtent de_gap : x_text_gap_list) {
1254
1255 int deg_xl = de_gap.min;
1256 int deg_xr = de_gap.max;
1257
1258 ArrayList<XRawItem> grouped_raw_text_list = new ArrayList<XRawItem>();
1259
1260 int y_span_line_count = 0;
1261
1262 for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) {
1263
1264 List<XItem> x_item_list = y_top_edge.x_items.getXItemList();
1265
1266 boolean used_x_raw_text = false;
1267
1268 for (XItem x_item : x_item_list) {
1269
1270 if (x_item instanceof XRawItem) {
1271 XRawItem x_raw_item = (XRawItem) x_item;
1272
1273 int xri_xl = x_raw_item.getBoundingXLeft();
1274 int xri_xr = x_raw_item.getBoundingXRight();
1275
1276 if ((xri_xl >= deg_xl) && (xri_xr <= deg_xr)) {
1277 grouped_raw_text_list.add(x_raw_item);
1278 used_x_raw_text = true;
1279 }
1280 }
1281 }
1282
1283 if (used_x_raw_text) {
1284 y_span_line_count++;
1285 }
1286 }
1287
1288 // Only interested in implicitly boxing if the formed group is used
1289 // over 2 or more y-span lines
1290 if (y_span_line_count >= 2) {
1291 implicit_box_list.add(grouped_raw_text_list);
1292 }
1293 }
1294
1295 // System.err.println("*** Number of implicit groups found: " +
1296 // implicit_box_list.size());
1297
1298 for (ArrayList<XRawItem> implicit_x_item_list : implicit_box_list) {
1299
1300 for (YOverlappingItemsTopEdge y_top_edge : y_span_overlap) {
1301
1302 List<XItem> yspan_x_item_list = y_top_edge.x_items
1303 .getXItemList();
1304
1305 // Remove all the XRawItems from the current y-span
1306 yspan_x_item_list.removeAll(implicit_x_item_list);
1307
1308 }
1309
1310 // Create a list of original Text items
1311 // (OK that they are not y-ordered)
1312 ArrayList<Item> implicit_item_list = new ArrayList<Item>();
1313 for (XRawItem x_raw_item : implicit_x_item_list) {
1314 Item item = (Text) x_raw_item.item;
1315 implicit_item_list.add(item);
1316 }
1317
1318 // Now insert them as their own boxed up items
1319 XGroupItem implicit_xgroup = new XGroupItem(frame,
1320 implicit_item_list);
1321 this.mapInItem(implicit_xgroup);
1322 }
1323
1324 }
1325
1326 protected void implicitBoxing(XGroupItem xgroup_pivot_item,
1327 List<XGroupItem> xgroup_item_list) {
1328
1329 // Implicit boxing is needed if there are two or more raw-text items
1330 // that overlap in y-span-map of the given xgroup_item
1331
1332 // First establish if there is any kind of multiple overlap
1333 if (multipleYSpanOverlap(xgroup_pivot_item)) {
1334
1335 // Overlap could be with other boxed items, so need to investigate
1336 // further
1337
1338 // Do this by working out if there are any raw-text items lurking
1339 // between the pivot xgroup_item
1340 // and the list of other xgroup_items
1341
1342 // Step 1: Get list intersection (y-dim) of this group item, with
1343 // any
1344 // other group items in xgroup_item_list
1345 ArrayList<XGroupItem> y_overlapping_xgroup_item_list = calcXGroupYExtentIntersection(
1346 xgroup_pivot_item, xgroup_item_list);
1347
1348 // Step 2: Work out where the gaps are between the y-overlapping
1349 // boxes in the x-dimension
1350 ArrayList<DimensionExtent> x_text_gap_list = calcXGroupXGaps(
1351 xgroup_pivot_item, y_overlapping_xgroup_item_list);
1352
1353 // Step 3: Remove any sequences of raw-text items that fall in the
1354 // gaps from YSpan, box them up, and reinsert
1355 boxMultipleXRawItemsInGaps(xgroup_pivot_item, x_text_gap_list);
1356
1357 }
1358 }
1359
1360 public void mapInXGroupItemsRecursive(List<XGroupItem> xgroup_item_list) {
1361
1362 // Map in all the items in the given list:
1363 for (XGroupItem xgroup_item : xgroup_item_list) {
1364 if (!xgroup_item.isOriginalOutOfFlowItem()) {
1365
1366 // See if any implicit boxing of raw-text items is needed
1367 if (doImplicitBoxing) {
1368 implicitBoxing(xgroup_item, xgroup_item_list);
1369 }
1370 mapInItem(xgroup_item); // Map in this x-group-item
1371 }
1372 }
1373
1374 // Now recursively work through each item's nested x-groups
1375 for (XGroupItem xgroup_item : xgroup_item_list) {
1376
1377 List<XGroupItem> nested_xgroup_item_list = xgroup_item
1378 .getGroupedItemList();
1379
1380 if (nested_xgroup_item_list.size() > 0) {
1381 xgroup_item.mapInXGroupItemsRecursive(nested_xgroup_item_list);
1382 }
1383 }
1384 }
1385
1386 public List<Item[]> getYXOverlappingItemListLines() {
1387 final List<XRawItem> yxOverlappingXRawItemList = getYXOverlappingXRawItemList(true);
1388 final List<Item[]> lines = new ArrayList<Item[]>();
1389 final List<XRawItem> currentLineXItem = new ArrayList<XRawItem>();
1390 for (final XRawItem xitem : yxOverlappingXRawItemList) {
1391 if (xitem.getItem() == GROUPSEP_START)
1392 continue;
1393 else if (xitem.getItem() == GROUPSEP_END) {
1394 if (currentLineXItem.isEmpty())
1395 continue;
1396 else {
1397 final List<Item> ln = new ArrayList<Item>();
1398 for (final XRawItem xrawitem : currentLineXItem)
1399 ln.add(xrawitem.getItem());
1400 lines.add(ln.toArray(new Item[] {}));
1401 currentLineXItem.clear();
1402 }
1403 } else {
1404 if (currentLineXItem.isEmpty())
1405 currentLineXItem.add(xitem);
1406 else {
1407 final YOverlappingItemsSpan[] itemSpanArray = xitem
1408 .getGroup().yitems_span_array;
1409 final int index = xitem.getBoundingYTop()
1410 - xitem.getGroup().getBoundingYTop();
1411 final YOverlappingItemsSpan itemSpan = itemSpanArray[index];
1412 final List<XItem> xOrderedList = itemSpan instanceof YOverlappingItemsTopEdge ? ((YOverlappingItemsTopEdge) itemSpan)
1413 .getXOrderedLine().getXItemList()
1414 : ((YOverlappingItemsShadow) itemSpan).getTopEdge()
1415 .getXOrderedLine().getXItemList();
1416 if (!xOrderedList.contains(currentLineXItem.get(0))) {
1417 if (!currentLineXItem.isEmpty()) {
1418 final List<Item> ln = new ArrayList<Item>();
1419 for (final XRawItem xrawitem : currentLineXItem)
1420 ln.add(xrawitem.getItem());
1421 lines.add(ln.toArray(new Item[] {}));
1422 currentLineXItem.clear();
1423 }
1424 currentLineXItem.add(xitem);
1425 } else {
1426 currentLineXItem.add(xitem);
1427 }
1428 }
1429 }
1430 }
1431 if (!currentLineXItem.isEmpty()) {
1432 final List<Item> ln = new ArrayList<Item>();
1433 for (final XRawItem xrawitem : currentLineXItem)
1434 ln.add(xrawitem.getItem());
1435 lines.add(ln.toArray(new Item[] {}));
1436 currentLineXItem.clear();
1437 }
1438 return lines;
1439 }
1440
1441 public ArrayList<Item> getYXOverlappingItemList(boolean separateGroups) {
1442 final List<XRawItem> yxOverlappingXItemList = getYXOverlappingXRawItemList(separateGroups);
1443 final ArrayList<Item> yxOverlappingItemList = new ArrayList<Item>();
1444 for (final XRawItem xitem : yxOverlappingXItemList)
1445 yxOverlappingItemList.add(xitem.getItem());
1446 return yxOverlappingItemList;
1447 }
1448
1449 public List<XRawItem> getYXOverlappingXRawItemList(
1450 final boolean separateGroups) {
1451 ArrayList<XRawItem> overlapping_y_ordered_items = new ArrayList<XRawItem>();
1452
1453 for (int y = 0; y < y_span_height; y++) {
1454
1455 YOverlappingItemsSpan item_span = yitems_span_array[y];
1456
1457 if (item_span != null) {
1458
1459 if (item_span instanceof YOverlappingItemsTopEdge) {
1460
1461 YOverlappingItemsTopEdge item_span_top_edge = (YOverlappingItemsTopEdge) item_span;
1462 XOrderedLine xitem_line = item_span_top_edge
1463 .getXOrderedLine();
1464
1465 for (XItem xspan : xitem_line.getXItemList()) {
1466 if (xspan instanceof XRawItem) {
1467 XRawItem xitem_span = (XRawItem) xspan;
1468 // Item item = xitem_span.getItem();
1469 //
1470 // overlapping_y_ordered_items.add(item);
1471 overlapping_y_ordered_items.add(xitem_span);
1472 } else {
1473 // Must be an XGroupItem => recursive call on xspan
1474 // item
1475
1476 XGroupItem nested_group_item = (XGroupItem) xspan;
1477 List<XRawItem> nested_overlapping_items = nested_group_item
1478 .getYXOverlappingXRawItemList(separateGroups);
1479 if (separateGroups) {
1480 overlapping_y_ordered_items.add(new XRawItem(
1481 GROUPSEP_START, this));
1482 }
1483 overlapping_y_ordered_items
1484 .addAll(nested_overlapping_items);
1485 if (separateGroups) {
1486 overlapping_y_ordered_items.add(new XRawItem(
1487 GROUPSEP_END, this));
1488 }
1489 }
1490 }
1491 }
1492 }
1493 }
1494
1495 return overlapping_y_ordered_items;
1496 }
1497
1498 public ArrayList<Item> getYXOverlappingItemList() {
1499 return getYXOverlappingItemList(false);
1500 }
1501
1502 public String toString() {
1503 return "XGroupItem";
1504 }
1505}
Note: See TracBrowser for help on using the repository browser.