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

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

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