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