1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.compress.harmony.pack200;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.StringReader;
24 import java.io.UncheckedIOException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29
30 import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
31 import org.apache.commons.compress.utils.ParsingUtils;
32 import org.objectweb.asm.Label;
33
34
35
36
37
38 public class NewAttributeBands extends BandSet {
39
40
41
42
43
44 public interface AttributeLayoutElement {
45
46 void addAttributeToBand(NewAttribute attribute, InputStream inputStream);
47
48 void pack(OutputStream ouputStream) throws IOException, Pack200Exception;
49
50 void renumberBci(IntList bciRenumbering, Map<Label, Integer> labelsToOffsets);
51
52 }
53
54 public class Call extends LayoutElement {
55
56 private final int callableIndex;
57 private Callable callable;
58
59 public Call(final int callableIndex) {
60 this.callableIndex = callableIndex;
61 }
62
63 @Override
64 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
65 callable.addAttributeToBand(attribute, inputStream);
66 if (callableIndex < 1) {
67 callable.addBackwardsCall();
68 }
69 }
70
71 public Callable getCallable() {
72 return callable;
73 }
74
75 public int getCallableIndex() {
76 return callableIndex;
77 }
78
79 @Override
80 public void pack(final OutputStream outputStream) {
81
82 }
83
84 @Override
85 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
86
87 }
88
89 public void setCallable(final Callable callable) {
90 this.callable = callable;
91 if (callableIndex < 1) {
92 callable.setBackwardsCallable();
93 }
94 }
95 }
96
97 public class Callable implements AttributeLayoutElement {
98
99 private final List<LayoutElement> body;
100
101 private boolean isBackwardsCallable;
102
103 private int backwardsCallableIndex;
104
105 public Callable(final List<LayoutElement> body) {
106 this.body = body;
107 }
108
109 @Override
110 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
111 for (final AttributeLayoutElement element : body) {
112 element.addAttributeToBand(attribute, inputStream);
113 }
114 }
115
116 public void addBackwardsCall() {
117 backwardsCallCounts[backwardsCallableIndex]++;
118 }
119
120 public List<LayoutElement> getBody() {
121 return body;
122 }
123
124 public boolean isBackwardsCallable() {
125 return isBackwardsCallable;
126 }
127
128 @Override
129 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
130 for (final AttributeLayoutElement element : body) {
131 element.pack(outputStream);
132 }
133 }
134
135 @Override
136 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
137 for (final AttributeLayoutElement element : body) {
138 element.renumberBci(bciRenumbering, labelsToOffsets);
139 }
140 }
141
142
143
144
145 public void setBackwardsCallable() {
146 this.isBackwardsCallable = true;
147 }
148
149 public void setBackwardsCallableIndex(final int backwardsCallableIndex) {
150 this.backwardsCallableIndex = backwardsCallableIndex;
151 }
152 }
153
154 public class Integral extends LayoutElement {
155
156 private final String tag;
157
158 private final List band = new ArrayList();
159 private final BHSDCodec defaultCodec;
160
161
162 private Integral previousIntegral;
163 private int previousPValue;
164
165 public Integral(final String tag) {
166 this.tag = tag;
167 this.defaultCodec = getCodec(tag);
168 }
169
170 public Integral(final String tag, final Integral previousIntegral) {
171 this.tag = tag;
172 this.defaultCodec = getCodec(tag);
173 this.previousIntegral = previousIntegral;
174 }
175
176 @Override
177 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
178 Object val = null;
179 int value = 0;
180 if (tag.equals("B") || tag.equals("FB")) {
181 value = readInteger(1, inputStream) & 0xFF;
182 } else if (tag.equals("SB")) {
183 value = readInteger(1, inputStream);
184 } else if (tag.equals("H") || tag.equals("FH")) {
185 value = readInteger(2, inputStream) & 0xFFFF;
186 } else if (tag.equals("SH")) {
187 value = readInteger(2, inputStream);
188 } else if (tag.equals("I") || tag.equals("FI") || tag.equals("SI")) {
189 value = readInteger(4, inputStream);
190 } else if (tag.equals("V") || tag.equals("FV") || tag.equals("SV")) {
191
192 } else if (tag.startsWith("PO") || tag.startsWith("OS")) {
193 final char uint_type = tag.substring(2).toCharArray()[0];
194 final int length = getLength(uint_type);
195 value = readInteger(length, inputStream);
196 value += previousIntegral.previousPValue;
197 val = attribute.getLabel(value);
198 previousPValue = value;
199 } else if (tag.startsWith("P")) {
200 final char uint_type = tag.substring(1).toCharArray()[0];
201 final int length = getLength(uint_type);
202 value = readInteger(length, inputStream);
203 val = attribute.getLabel(value);
204 previousPValue = value;
205 } else if (tag.startsWith("O")) {
206 final char uint_type = tag.substring(1).toCharArray()[0];
207 final int length = getLength(uint_type);
208 value = readInteger(length, inputStream);
209 value += previousIntegral.previousPValue;
210 val = attribute.getLabel(value);
211 previousPValue = value;
212 }
213 if (val == null) {
214 val = Integer.valueOf(value);
215 }
216 band.add(val);
217 }
218
219 public String getTag() {
220 return tag;
221 }
222
223 public int latestValue() {
224 return ((Integer) band.get(band.size() - 1)).intValue();
225 }
226
227 @Override
228 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
229 PackingUtils.log("Writing new attribute bands...");
230 final byte[] encodedBand = encodeBandInt(tag, integerListToArray(band), defaultCodec);
231 outputStream.write(encodedBand);
232 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + band.size() + "]");
233 }
234
235 @Override
236 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
237 if (tag.startsWith("O") || tag.startsWith("PO")) {
238 renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets);
239 } else if (tag.startsWith("P")) {
240 for (int i = band.size() - 1; i >= 0; i--) {
241 final Object label = band.get(i);
242 if (label instanceof Integer) {
243 break;
244 }
245 if (label instanceof Label) {
246 band.remove(i);
247 final Integer bytecodeIndex = labelsToOffsets.get(label);
248 band.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue())));
249 }
250 }
251 }
252 }
253
254 private void renumberOffsetBci(final List relative, final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
255 for (int i = band.size() - 1; i >= 0; i--) {
256 final Object label = band.get(i);
257 if (label instanceof Integer) {
258 break;
259 }
260 if (label instanceof Label) {
261 band.remove(i);
262 final Integer bytecodeIndex = labelsToOffsets.get(label);
263 final Integer renumberedOffset = Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - ((Integer) relative.get(i)).intValue());
264 band.add(i, renumberedOffset);
265 }
266 }
267 }
268
269 }
270
271 public abstract class LayoutElement implements AttributeLayoutElement {
272
273 protected int getLength(final char uint_type) {
274 int length = 0;
275 switch (uint_type) {
276 case 'B':
277 length = 1;
278 break;
279 case 'H':
280 length = 2;
281 break;
282 case 'I':
283 length = 4;
284 break;
285 case 'V':
286 length = 0;
287 break;
288 }
289 return length;
290 }
291 }
292
293
294
295
296 public class Reference extends LayoutElement {
297
298 private final String tag;
299
300 private final List<ConstantPoolEntry> band = new ArrayList<>();
301
302 private final boolean nullsAllowed;
303
304 public Reference(final String tag) {
305 this.tag = tag;
306 nullsAllowed = tag.indexOf('N') != -1;
307 }
308
309 @Override
310 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
311 final int index = readInteger(4, inputStream);
312 if (tag.startsWith("RC")) {
313 band.add(cpBands.getCPClass(attribute.readClass(index)));
314 } else if (tag.startsWith("RU")) {
315 band.add(cpBands.getCPUtf8(attribute.readUTF8(index)));
316 } else if (tag.startsWith("RS")) {
317 band.add(cpBands.getCPSignature(attribute.readUTF8(index)));
318 } else {
319 band.add(cpBands.getConstant(attribute.readConst(index)));
320 }
321
322 }
323
324 public String getTag() {
325 return tag;
326 }
327
328 @Override
329 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
330 int[] ints;
331 if (nullsAllowed) {
332 ints = cpEntryOrNullListToArray(band);
333 } else {
334 ints = cpEntryListToArray(band);
335 }
336 final byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5);
337 outputStream.write(encodedBand);
338 PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + ints.length + "]");
339 }
340
341 @Override
342 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
343
344 }
345
346 }
347
348
349
350
351 public class Replication extends LayoutElement {
352
353 private final Integral countElement;
354
355 private final List<LayoutElement> layoutElements = new ArrayList<>();
356
357 public Replication(final String tag, final String contents) throws IOException {
358 this.countElement = new Integral(tag);
359 final StringReader stream = new StringReader(contents);
360 LayoutElement e;
361 while ((e = readNextLayoutElement(stream)) != null) {
362 layoutElements.add(e);
363 }
364 }
365
366 @Override
367 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
368 countElement.addAttributeToBand(attribute, inputStream);
369 final int count = countElement.latestValue();
370 for (int i = 0; i < count; i++) {
371 for (final AttributeLayoutElement layoutElement : layoutElements) {
372 layoutElement.addAttributeToBand(attribute, inputStream);
373 }
374 }
375 }
376
377 public Integral getCountElement() {
378 return countElement;
379 }
380
381 public List<LayoutElement> getLayoutElements() {
382 return layoutElements;
383 }
384
385 @Override
386 public void pack(final OutputStream out) throws IOException, Pack200Exception {
387 countElement.pack(out);
388 for (final AttributeLayoutElement layoutElement : layoutElements) {
389 layoutElement.pack(out);
390 }
391 }
392
393 @Override
394 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
395 for (final AttributeLayoutElement layoutElement : layoutElements) {
396 layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
397 }
398 }
399 }
400
401
402
403
404 public class Union extends LayoutElement {
405
406 private final Integral unionTag;
407 private final List<UnionCase> unionCases;
408 private final List<LayoutElement> defaultCaseBody;
409
410 public Union(final String tag, final List<UnionCase> unionCases, final List<LayoutElement> body) {
411 this.unionTag = new Integral(tag);
412 this.unionCases = unionCases;
413 this.defaultCaseBody = body;
414 }
415
416 @Override
417 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
418 unionTag.addAttributeToBand(attribute, inputStream);
419 final long tag = unionTag.latestValue();
420 boolean defaultCase = true;
421 for (final UnionCase unionCase : unionCases) {
422 if (unionCase.hasTag(tag)) {
423 defaultCase = false;
424 unionCase.addAttributeToBand(attribute, inputStream);
425 }
426 }
427 if (defaultCase) {
428 for (final LayoutElement layoutElement : defaultCaseBody) {
429 layoutElement.addAttributeToBand(attribute, inputStream);
430 }
431 }
432 }
433
434 public List<LayoutElement> getDefaultCaseBody() {
435 return defaultCaseBody;
436 }
437
438 public List<UnionCase> getUnionCases() {
439 return unionCases;
440 }
441
442 public Integral getUnionTag() {
443 return unionTag;
444 }
445
446 @Override
447 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
448 unionTag.pack(outputStream);
449 for (final UnionCase unionCase : unionCases) {
450 unionCase.pack(outputStream);
451 }
452 for (final LayoutElement element : defaultCaseBody) {
453 element.pack(outputStream);
454 }
455 }
456
457 @Override
458 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
459 for (final UnionCase unionCase : unionCases) {
460 unionCase.renumberBci(bciRenumbering, labelsToOffsets);
461 }
462 for (final LayoutElement element : defaultCaseBody) {
463 element.renumberBci(bciRenumbering, labelsToOffsets);
464 }
465 }
466 }
467
468
469
470
471 public class UnionCase extends LayoutElement {
472
473 private final List<LayoutElement> body;
474
475 private final List<Integer> tags;
476
477 public UnionCase(final List<Integer> tags) {
478 this.tags = tags;
479 this.body = Collections.EMPTY_LIST;
480 }
481
482 public UnionCase(final List<Integer> tags, final List<LayoutElement> body) {
483 this.tags = tags;
484 this.body = body;
485 }
486
487 @Override
488 public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
489 for (final LayoutElement element : body) {
490 element.addAttributeToBand(attribute, inputStream);
491 }
492 }
493
494 public List<LayoutElement> getBody() {
495 return body;
496 }
497
498 public boolean hasTag(final long l) {
499 return tags.contains(Integer.valueOf((int) l));
500 }
501
502 @Override
503 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
504 for (final LayoutElement element : body) {
505 element.pack(outputStream);
506 }
507 }
508
509 @Override
510 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
511 for (final LayoutElement element : body) {
512 element.renumberBci(bciRenumbering, labelsToOffsets);
513 }
514 }
515 }
516
517 protected List<AttributeLayoutElement> attributeLayoutElements;
518
519 private int[] backwardsCallCounts;
520
521 private final CpBands cpBands;
522
523 private final AttributeDefinition def;
524
525 private boolean usedAtLeastOnce;
526
527
528 private Integral lastPIntegral;
529
530 public NewAttributeBands(final int effort, final CpBands cpBands, final SegmentHeader header, final AttributeDefinition def) throws IOException {
531 super(effort, header);
532 this.def = def;
533 this.cpBands = cpBands;
534 parseLayout();
535 }
536
537 public void addAttribute(final NewAttribute attribute) {
538 usedAtLeastOnce = true;
539 final InputStream stream = new ByteArrayInputStream(attribute.getBytes());
540 for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
541 attributeLayoutElement.addAttributeToBand(attribute, stream);
542 }
543 }
544
545 public String getAttributeName() {
546 return def.name.getUnderlyingString();
547 }
548
549
550
551
552
553
554 private BHSDCodec getCodec(final String layoutElement) {
555 if (layoutElement.indexOf('O') >= 0) {
556 return Codec.BRANCH5;
557 }
558 if (layoutElement.indexOf('P') >= 0) {
559 return Codec.BCI5;
560 }
561 if (layoutElement.indexOf('S') >= 0 && !layoutElement.contains("KS")
562 && !layoutElement.contains("RS")) {
563 return Codec.SIGNED5;
564 }
565 if (layoutElement.indexOf('B') >= 0) {
566 return Codec.BYTE1;
567 }
568 return Codec.UNSIGNED5;
569 }
570
571 public int getFlagIndex() {
572 return def.index;
573 }
574
575
576
577
578
579
580
581
582 private StringReader getStreamUpToMatchingBracket(final StringReader reader) throws IOException {
583 final StringBuilder sb = new StringBuilder();
584 int foundBracket = -1;
585 while (foundBracket != 0) {
586 final int read = reader.read();
587 if (read == -1) {
588 break;
589 }
590 final char c = (char) read;
591 if (c == ']') {
592 foundBracket++;
593 }
594 if (c == '[') {
595 foundBracket--;
596 }
597 if (!(foundBracket == 0)) {
598 sb.append(c);
599 }
600 }
601 return new StringReader(sb.toString());
602 }
603
604 public boolean isUsedAtLeastOnce() {
605 return usedAtLeastOnce;
606 }
607
608 public int[] numBackwardsCalls() {
609 return backwardsCallCounts;
610 }
611
612 @Override
613 public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
614 for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
615 attributeLayoutElement.pack(outputStream);
616 }
617 }
618
619 private void parseLayout() throws IOException {
620 final String layout = def.layout.getUnderlyingString();
621 if (attributeLayoutElements == null) {
622 attributeLayoutElements = new ArrayList<>();
623 final StringReader reader = new StringReader(layout);
624 AttributeLayoutElement e;
625 while ((e = readNextAttributeElement(reader)) != null) {
626 attributeLayoutElements.add(e);
627 }
628 resolveCalls();
629 }
630 }
631
632
633
634
635
636
637
638
639 private List<LayoutElement> readBody(final StringReader reader) throws IOException {
640 final List<LayoutElement> layoutElements = new ArrayList<>();
641 LayoutElement e;
642 while ((e = readNextLayoutElement(reader)) != null) {
643 layoutElements.add(e);
644 }
645 return layoutElements;
646 }
647
648 private int readInteger(final int i, final InputStream inputStream) {
649 int result = 0;
650 for (int j = 0; j < i; j++) {
651 try {
652 result = result << 8 | inputStream.read();
653 } catch (final IOException e) {
654 throw new UncheckedIOException("Error reading unknown attribute", e);
655 }
656 }
657
658 if (i == 1) {
659 result = (byte) result;
660 }
661 if (i == 2) {
662 result = (short) result;
663 }
664 return result;
665 }
666
667 private AttributeLayoutElement readNextAttributeElement(final StringReader reader) throws IOException {
668 reader.mark(1);
669 final int next = reader.read();
670 if (next == -1) {
671 return null;
672 }
673 if (next == '[') {
674 return new Callable(readBody(getStreamUpToMatchingBracket(reader)));
675 }
676 reader.reset();
677 return readNextLayoutElement(reader);
678 }
679
680 private LayoutElement readNextLayoutElement(final StringReader reader) throws IOException {
681 final int nextChar = reader.read();
682 if (nextChar == -1) {
683 return null;
684 }
685
686 switch (nextChar) {
687
688 case 'B':
689 case 'H':
690 case 'I':
691 case 'V':
692 return new Integral(new String(new char[] { (char) nextChar }));
693 case 'S':
694 case 'F':
695 return new Integral(new String(new char[] { (char) nextChar, (char) reader.read() }));
696 case 'P':
697 reader.mark(1);
698 if (reader.read() != 'O') {
699 reader.reset();
700 lastPIntegral = new Integral("P" + (char) reader.read());
701 return lastPIntegral;
702 }
703 lastPIntegral = new Integral("PO" + (char) reader.read(), lastPIntegral);
704 return lastPIntegral;
705 case 'O':
706 reader.mark(1);
707 if (reader.read() != 'S') {
708 reader.reset();
709 return new Integral("O" + (char) reader.read(), lastPIntegral);
710 }
711 return new Integral("OS" + (char) reader.read(), lastPIntegral);
712
713
714 case 'N':
715 final char uint_type = (char) reader.read();
716 reader.read();
717 final String str = readUpToMatchingBracket(reader);
718 return new Replication("" + uint_type, str);
719
720
721 case 'T':
722 String int_type = String.valueOf((char) reader.read());
723 if (int_type.equals("S")) {
724 int_type += (char) reader.read();
725 }
726 final List<UnionCase> unionCases = new ArrayList<>();
727 UnionCase c;
728 while ((c = readNextUnionCase(reader)) != null) {
729 unionCases.add(c);
730 }
731 reader.read();
732 reader.read();
733 reader.read();
734 List<LayoutElement> body = null;
735 reader.mark(1);
736 final char next = (char) reader.read();
737 if (next != ']') {
738 reader.reset();
739 body = readBody(getStreamUpToMatchingBracket(reader));
740 }
741 return new Union(int_type, unionCases, body);
742
743
744 case '(':
745 final int number = readNumber(reader).intValue();
746 reader.read();
747 return new Call(number);
748
749 case 'K':
750 case 'R':
751 final StringBuilder string = new StringBuilder("").append((char) nextChar).append((char) reader.read());
752 final char nxt = (char) reader.read();
753 string.append(nxt);
754 if (nxt == 'N') {
755 string.append((char) reader.read());
756 }
757 return new Reference(string.toString());
758 }
759 return null;
760 }
761
762
763
764
765
766
767
768
769 private UnionCase readNextUnionCase(final StringReader reader) throws IOException {
770 reader.mark(2);
771 reader.read();
772 final int next = reader.read();
773 char ch = (char) next;
774 if (ch == ')' || next == -1) {
775 reader.reset();
776 return null;
777 }
778 reader.reset();
779 reader.read();
780 final List<Integer> tags = new ArrayList<>();
781 Integer nextTag;
782 do {
783 nextTag = readNumber(reader);
784 if (nextTag != null) {
785 tags.add(nextTag);
786 reader.read();
787 }
788 } while (nextTag != null);
789 reader.read();
790 reader.mark(1);
791 ch = (char) reader.read();
792 if (ch == ']') {
793 return new UnionCase(tags);
794 }
795 reader.reset();
796 return new UnionCase(tags, readBody(getStreamUpToMatchingBracket(reader)));
797 }
798
799
800
801
802
803
804
805
806 private Integer readNumber(final StringReader stream) throws IOException {
807 stream.mark(1);
808 final char first = (char) stream.read();
809 final boolean negative = first == '-';
810 if (!negative) {
811 stream.reset();
812 }
813 stream.mark(100);
814 int i;
815 int length = 0;
816 while ((i = stream.read()) != -1 && Character.isDigit((char) i)) {
817 length++;
818 }
819 stream.reset();
820 if (length == 0) {
821 return null;
822 }
823 final char[] digits = new char[length];
824 final int read = stream.read(digits);
825 if (read != digits.length) {
826 throw new IOException("Error reading from the input stream");
827 }
828 return ParsingUtils.parseIntValue((negative ? "-" : "") + new String(digits));
829 }
830
831
832
833
834
835
836
837
838 private String readUpToMatchingBracket(final StringReader reader) throws IOException {
839 final StringBuilder sb = new StringBuilder();
840 int foundBracket = -1;
841 while (foundBracket != 0) {
842 final int read = reader.read();
843 if (read == -1) {
844 break;
845 }
846 final char c = (char) read;
847 if (c == ']') {
848 foundBracket++;
849 }
850 if (c == '[') {
851 foundBracket--;
852 }
853 if (!(foundBracket == 0)) {
854 sb.append(c);
855 }
856 }
857 return sb.toString();
858 }
859
860
861
862
863
864
865
866 public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
867 for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
868 attributeLayoutElement.renumberBci(bciRenumbering, labelsToOffsets);
869 }
870 }
871
872
873
874
875
876
877 private void resolveCalls() {
878 for (int i = 0; i < attributeLayoutElements.size(); i++) {
879 final AttributeLayoutElement element = attributeLayoutElements.get(i);
880 if (element instanceof Callable) {
881 final Callable callable = (Callable) element;
882 final List<LayoutElement> body = callable.body;
883 for (final LayoutElement layoutElement : body) {
884
885 resolveCallsForElement(i, callable, layoutElement);
886 }
887 }
888 }
889 int backwardsCallableIndex = 0;
890 for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
891 if (attributeLayoutElement instanceof Callable) {
892 final Callable callable = (Callable) attributeLayoutElement;
893 if (callable.isBackwardsCallable) {
894 callable.setBackwardsCallableIndex(backwardsCallableIndex);
895 backwardsCallableIndex++;
896 }
897 }
898 }
899 backwardsCallCounts = new int[backwardsCallableIndex];
900 }
901
902 private void resolveCallsForElement(final int i, final Callable currentCallable, final LayoutElement layoutElement) {
903 if (layoutElement instanceof Call) {
904 final Call call = (Call) layoutElement;
905 int index = call.callableIndex;
906 if (index == 0) {
907 call.setCallable(currentCallable);
908 } else if (index > 0) {
909 for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
910 final AttributeLayoutElement el = attributeLayoutElements.get(k);
911 if (el instanceof Callable) {
912 index--;
913 if (index == 0) {
914 call.setCallable((Callable) el);
915 break;
916 }
917 }
918 }
919 } else {
920 for (int k = i - 1; k >= 0; k--) {
921 final AttributeLayoutElement el = attributeLayoutElements.get(k);
922 if (el instanceof Callable) {
923 index++;
924 if (index == 0) {
925 call.setCallable((Callable) el);
926 break;
927 }
928 }
929 }
930 }
931 } else if (layoutElement instanceof Replication) {
932 final List<LayoutElement> children = ((Replication) layoutElement).layoutElements;
933 for (final LayoutElement child : children) {
934 resolveCallsForElement(i, currentCallable, child);
935 }
936 }
937 }
938
939 }