NewAttributeBands.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.commons.compress.harmony.pack200;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.compress.harmony.internal.AttributeLayoutParser;
import org.apache.commons.compress.harmony.internal.AttributeLayoutParser.UnionCaseData;
import org.apache.commons.compress.harmony.internal.AttributeLayoutUtils;
import org.apache.commons.compress.harmony.pack200.AttributeDefinitionBands.AttributeDefinition;
import org.apache.commons.lang3.IntegerRange;
import org.objectweb.asm.Label;
/**
* Sets of bands relating to a non-predefined attribute that has had a layout definition given to pack200 (for example via one of the -C, -M, -F or -D command
* line options)
*
* @see <a href="https://docs.oracle.com/en/java/javase/13/docs/specs/pack-spec.html">Pack200: A Packed Class Deployment Format For Java Applications</a>
*/
public class NewAttributeBands extends BandSet {
/**
* An AttributeLayoutElement is a part of an attribute layout and has one or more bands associated with it, which transmit the AttributeElement data for
* successive Attributes of this type.
*/
public interface AttributeLayoutElement {
/**
* Adds an attribute to the band.
*
* @param attribute the attribute.
* @param inputStream the input stream.
*/
void addAttributeToBand(NewAttribute attribute, InputStream inputStream);
/**
* Packs the band to the output stream.
*
* @param ouputStream the output stream.
* @throws IOException if an I/O error occurs.
* @throws Pack200Exception if a Pack200 error occurs.
*/
void pack(OutputStream ouputStream) throws IOException, Pack200Exception;
/**
* Renumbers bytecode indexes.
*
* @param bciRenumbering the bytecode index renumbering.
* @param labelsToOffsets the labels to offsets map.
*/
void renumberBci(IntList bciRenumbering, Map<Label, Integer> labelsToOffsets);
}
private class AttributeLayoutFactory implements AttributeLayoutParser.Factory<LayoutElement> {
/**
* The last P-type integral seen (for use with subsequent O and PO types)
*/
private Integral lastPIntegral;
@Override
public LayoutElement createCall(final int callableIndex) {
return new Call(callableIndex);
}
@Override
public LayoutElement createCallable(final List<LayoutElement> body) throws Pack200Exception {
return new Callable(body);
}
@Override
public LayoutElement createIntegral(final String tag) {
final Integral integral;
if (tag.startsWith("O") || tag.startsWith("PO")) {
integral = new Integral(tag, lastPIntegral);
} else {
integral = new Integral(tag);
}
if (tag.startsWith("P")) {
lastPIntegral = integral;
}
return integral;
}
@Override
public LayoutElement createReference(final String tag) {
return new Reference(tag);
}
@Override
public LayoutElement createReplication(final String unsignedInt, final List<LayoutElement> body) throws Pack200Exception {
return new Replication(unsignedInt, body);
}
@Override
public LayoutElement createUnion(final String anyInt, final List<UnionCaseData<LayoutElement>> cases, final List<LayoutElement> body)
throws Pack200Exception {
final List<UnionCase> unionCases = new ArrayList<>();
for (final UnionCaseData<LayoutElement> unionCaseData : cases) {
unionCases.add(new UnionCase(unionCaseData.tagRanges, unionCaseData.body, false));
}
return new Union(anyInt, unionCases, body);
}
}
/**
* Represents a call to a callable layout element.
*/
public class Call extends LayoutElement {
private final int callableIndex;
private Callable callable;
/**
* Constructs a new Call.
*
* @param callableIndex the callable index.
*/
public Call(final int callableIndex) {
this.callableIndex = callableIndex;
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
callable.addAttributeToBand(attribute, inputStream);
if (callableIndex < 1) {
callable.addBackwardsCall();
}
}
/**
* Gets the callable.
*
* @return the callable.
*/
public Callable getCallable() {
return callable;
}
/**
* Gets the callable index.
*
* @return the callable index.
*/
public int getCallableIndex() {
return callableIndex;
}
@Override
public void pack(final OutputStream outputStream) {
// do nothing here as pack will be called on the callable at another time
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
// do nothing here as renumberBci will be called on the callable at another time
}
/**
* Sets the callable.
*
* @param callable the callable.
*/
public void setCallable(final Callable callable) {
this.callable = callable;
if (callableIndex < 1) {
callable.setBackwardsCallable();
}
}
}
/**
* Callable layout element that can be referenced and reused.
*/
public class Callable extends LayoutElement {
private final List<LayoutElement> body;
private boolean isBackwardsCallable;
private int backwardsCallableIndex;
/**
* Constructs a new Callable layout element with the given body.
*
* @param body the body of the callable.
* @throws Pack200Exception If the body is empty.
*/
public Callable(final List<LayoutElement> body) throws Pack200Exception {
if (body.isEmpty()) {
throw new Pack200Exception("Corrupted Pack200 archive: Callable body is empty");
}
this.body = body;
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
for (final AttributeLayoutElement element : body) {
element.addAttributeToBand(attribute, inputStream);
}
}
/**
* Adds a backwards call count.
*/
public void addBackwardsCall() {
backwardsCallCounts[backwardsCallableIndex]++;
}
/**
* Gets the body of this callable.
*
* @return the body elements.
*/
public List<LayoutElement> getBody() {
return body;
}
/**
* Tests whether this is a backwards callable.
*
* @return true if backwards callable.
*/
public boolean isBackwardsCallable() {
return isBackwardsCallable;
}
@Override
public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
for (final AttributeLayoutElement element : body) {
element.pack(outputStream);
}
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
for (final AttributeLayoutElement element : body) {
element.renumberBci(bciRenumbering, labelsToOffsets);
}
}
/**
* Tells this Callable that it is a backwards callable
*/
public void setBackwardsCallable() {
this.isBackwardsCallable = true;
}
/**
* Sets the backwards callable index.
*
* @param backwardsCallableIndex the index.
*/
public void setBackwardsCallableIndex(final int backwardsCallableIndex) {
this.backwardsCallableIndex = backwardsCallableIndex;
}
}
/**
* Integral layout element for integer values.
*/
public class Integral extends LayoutElement {
private final String tag;
private final List band = new ArrayList();
private final BHSDCodec defaultCodec;
// used for bytecode offsets (OH and POH)
private Integral previousIntegral;
private int previousPValue;
/**
* Constructs a new Integral layout element with the given tag.
*
* @param tag The tag.
* @throws IllegalArgumentException If the tag is invalid.
*/
public Integral(final String tag) {
this.tag = AttributeLayoutUtils.checkIntegralTag(tag);
this.defaultCodec = getCodec(tag);
}
/**
* Constructs a new Integral layout element with the given tag.
*
* @param tag The tag.
* @param previousIntegral The previous integral (for PO and OS types).
* @throws IllegalArgumentException If the tag is invalid.
*/
public Integral(final String tag, final Integral previousIntegral) {
this.tag = AttributeLayoutUtils.checkIntegralTag(tag);
this.defaultCodec = getCodec(tag);
this.previousIntegral = previousIntegral;
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
Object val = null;
int value = 0;
switch (tag) {
case "B":
case "FB":
value = readInteger(1, inputStream) & 0xFF; // unsigned byte
break;
case "SB":
value = readInteger(1, inputStream);
break;
case "H":
case "FH":
value = readInteger(2, inputStream) & 0xFFFF; // unsigned short
break;
case "SH":
value = readInteger(2, inputStream);
break;
case "I":
case "FI":
case "SI":
value = readInteger(4, inputStream);
break;
case "V":
case "FV":
case "SV":
break;
default:
if (tag.startsWith("PO") || tag.startsWith("OS")) {
final char uint_type = tag.substring(2).toCharArray()[0];
final int length = getLength(uint_type);
value = readInteger(length, inputStream);
value += previousIntegral.previousPValue;
val = attribute.getLabel(value);
previousPValue = value;
} else if (tag.startsWith("P")) {
final char uint_type = tag.substring(1).toCharArray()[0];
final int length = getLength(uint_type);
value = readInteger(length, inputStream);
val = attribute.getLabel(value);
previousPValue = value;
} else if (tag.startsWith("O")) {
final char uint_type = tag.substring(1).toCharArray()[0];
final int length = getLength(uint_type);
value = readInteger(length, inputStream);
value += previousIntegral.previousPValue;
val = attribute.getLabel(value);
previousPValue = value;
}
break;
}
if (val == null) {
val = Integer.valueOf(value);
}
band.add(val);
}
/**
* Gets the tag of this integral.
*
* @return the tag.
*/
public String getTag() {
return tag;
}
/**
* Gets the latest value added to the band.
*
* @return the latest value.
*/
public int latestValue() {
return ((Integer) band.get(band.size() - 1)).intValue();
}
@Override
public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
PackingUtils.log("Writing new attribute bands...");
final byte[] encodedBand = encodeBandInt(tag, integerListToArray(band), defaultCodec);
outputStream.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + band.size() + "]");
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
if (tag.startsWith("O") || tag.startsWith("PO")) {
renumberOffsetBci(previousIntegral.band, bciRenumbering, labelsToOffsets);
} else if (tag.startsWith("P")) {
for (int i = band.size() - 1; i >= 0; i--) {
final Object label = band.get(i);
if (label instanceof Integer) {
break;
}
if (label instanceof Label) {
band.remove(i);
final Integer bytecodeIndex = labelsToOffsets.get(label);
band.add(i, Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue())));
}
}
}
}
private void renumberOffsetBci(final List relative, final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
for (int i = band.size() - 1; i >= 0; i--) {
final Object label = band.get(i);
if (label instanceof Integer) {
break;
}
if (label instanceof Label) {
band.remove(i);
final Integer bytecodeIndex = labelsToOffsets.get(label);
final Integer renumberedOffset = Integer.valueOf(bciRenumbering.get(bytecodeIndex.intValue()) - ((Integer) relative.get(i)).intValue());
band.add(i, renumberedOffset);
}
}
}
}
/**
* Abstract base class for layout elements.
*/
public abstract class LayoutElement implements AttributeLayoutElement {
/**
* Constructs a new LayoutElement.
*/
public LayoutElement() {
}
/**
* Gets the length for the given unsigned type.
*
* @param uint_type the unsigned type character.
* @return the length in bytes.
*/
protected int getLength(final char uint_type) {
int length = 0;
switch (uint_type) {
case 'B':
length = 1;
break;
case 'H':
length = 2;
break;
case 'I':
length = 4;
break;
case 'V':
length = 0;
break;
}
return length;
}
}
/**
* Constant Pool Reference
*/
public class Reference extends LayoutElement {
private final String tag;
private final List<ConstantPoolEntry> band = new ArrayList<>();
private final boolean nullsAllowed;
/**
* Constructs a new Reference layout element with the given tag.
*
* @param tag The tag.
* @throws IllegalArgumentException If the tag is invalid.
*/
public Reference(final String tag) {
this.tag = AttributeLayoutUtils.checkReferenceTag(tag);
nullsAllowed = tag.indexOf('N') != -1;
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
final int index = readInteger(4, inputStream);
if (tag.startsWith("RC")) { // Class
band.add(cpBands.getCPClass(attribute.readClass(index)));
} else if (tag.startsWith("RU")) { // UTF8 String
band.add(cpBands.getCPUtf8(attribute.readUTF8(index)));
} else if (tag.startsWith("RS")) { // Signature
band.add(cpBands.getCPSignature(attribute.readUTF8(index)));
} else { // Constant
band.add(cpBands.getConstant(attribute.readConst(index)));
}
// TODO method and field references
}
/**
* Gets the tag of this reference.
*
* @return the tag.
*/
public String getTag() {
return tag;
}
@Override
public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
final int[] ints;
if (nullsAllowed) {
ints = cpEntryOrNullListToArray(band);
} else {
ints = cpEntryListToArray(band);
}
final byte[] encodedBand = encodeBandInt(tag, ints, Codec.UNSIGNED5);
outputStream.write(encodedBand);
PackingUtils.log("Wrote " + encodedBand.length + " bytes from " + tag + "[" + ints.length + "]");
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
// nothing to do here
}
}
/**
* A replication is an array of layout elements, with an associated count
*/
public class Replication extends LayoutElement {
private final Integral countElement;
private final List<LayoutElement> layoutElements;
private Replication(final String tag, final List<LayoutElement> contents) throws Pack200Exception {
this.countElement = new Integral(tag);
this.layoutElements = contents;
if (layoutElements.isEmpty()) {
throw new Pack200Exception("Corrupted Pack200 archive: Replication body is empty");
}
}
/**
* Constructs a new Replication layout element.
*
* @param tag the tag of the Integral element.
* @param contents the contents of the replication.
* @throws IllegalArgumentException If the tag is invalid or the contents are empty.
* @throws Pack200Exception If the contents are invalid.
*/
public Replication(final String tag, final String contents) throws Pack200Exception {
this(tag, AttributeLayoutUtils.readBody(contents, attributeLayoutFactory));
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
countElement.addAttributeToBand(attribute, inputStream);
final int count = countElement.latestValue();
for (int i = 0; i < count; i++) {
for (final AttributeLayoutElement layoutElement : layoutElements) {
layoutElement.addAttributeToBand(attribute, inputStream);
}
}
}
/**
* Gets the count element.
*
* @return the count element.
*/
public Integral getCountElement() {
return countElement;
}
/**
* Gets the layout elements.
*
* @return the layout elements.
*/
public List<LayoutElement> getLayoutElements() {
return layoutElements;
}
@Override
public void pack(final OutputStream out) throws IOException, Pack200Exception {
countElement.pack(out);
for (final AttributeLayoutElement layoutElement : layoutElements) {
layoutElement.pack(out);
}
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
for (final AttributeLayoutElement layoutElement : layoutElements) {
layoutElement.renumberBci(bciRenumbering, labelsToOffsets);
}
}
}
/**
* A Union is a type of layout element where the tag value acts as a selector for one of the union cases
*/
public class Union extends LayoutElement {
private final Integral unionTag;
private final List<UnionCase> unionCases;
private final List<LayoutElement> defaultCaseBody;
/**
* Constructs a new Union layout element.
*
* @param tag the tag of the Integral element.
* @param unionCases the union cases.
* @param body the default case body.
* @throws IllegalArgumentException If the tag is invalid.
*/
public Union(final String tag, final List<UnionCase> unionCases, final List<LayoutElement> body) {
this.unionTag = new Integral(tag);
this.unionCases = unionCases;
this.defaultCaseBody = body;
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
unionTag.addAttributeToBand(attribute, inputStream);
final long tag = unionTag.latestValue();
boolean defaultCase = true;
for (final UnionCase unionCase : unionCases) {
if (unionCase.hasTag(tag)) {
defaultCase = false;
unionCase.addAttributeToBand(attribute, inputStream);
}
}
if (defaultCase) {
for (final LayoutElement layoutElement : defaultCaseBody) {
layoutElement.addAttributeToBand(attribute, inputStream);
}
}
}
/**
* Gets the default case body.
*
* @return the default case body.
*/
public List<LayoutElement> getDefaultCaseBody() {
return defaultCaseBody;
}
/**
* Gets the union cases.
*
* @return the union cases.
*/
public List<UnionCase> getUnionCases() {
return unionCases;
}
/**
* Gets the union tag.
*
* @return the union tag.
*/
public Integral getUnionTag() {
return unionTag;
}
@Override
public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
unionTag.pack(outputStream);
for (final UnionCase unionCase : unionCases) {
unionCase.pack(outputStream);
}
for (final LayoutElement element : defaultCaseBody) {
element.pack(outputStream);
}
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
for (final UnionCase unionCase : unionCases) {
unionCase.renumberBci(bciRenumbering, labelsToOffsets);
}
for (final LayoutElement element : defaultCaseBody) {
element.renumberBci(bciRenumbering, labelsToOffsets);
}
}
}
/**
* A Union case
*/
public class UnionCase extends LayoutElement {
private final List<IntegerRange> tagRanges;
private final List<LayoutElement> body;
/**
* Constructs a new UnionCase.
*
* @param tags the tags.
*/
public UnionCase(final List<Integer> tags) {
this(tags, Collections.emptyList());
}
/**
* Constructs a new UnionCase.
*
* @param tags the tags.
* @param body the body elements.
*/
public UnionCase(final List<Integer> tags, final List<LayoutElement> body) {
this(AttributeLayoutUtils.toRanges(tags), body, false);
}
private UnionCase(final List<IntegerRange> tagRanges, final List<LayoutElement> body, final boolean ignored) {
this.tagRanges = tagRanges;
this.body = body;
}
@Override
public void addAttributeToBand(final NewAttribute attribute, final InputStream inputStream) {
for (final LayoutElement element : body) {
element.addAttributeToBand(attribute, inputStream);
}
}
/**
* Gets the body of this union case.
*
* @return the body elements.
*/
public List<LayoutElement> getBody() {
return body;
}
/**
* Tests whether this union case has the given tag.
*
* @param l the tag value.
* @return true if this case matches the tag.
*/
public boolean hasTag(final long l) {
return AttributeLayoutUtils.unionCaseMatches(tagRanges, (int) l);
}
@Override
public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
for (final LayoutElement element : body) {
element.pack(outputStream);
}
}
@Override
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
for (final LayoutElement element : body) {
element.renumberBci(bciRenumbering, labelsToOffsets);
}
}
}
/**
* The attribute layout elements.
*/
protected List<LayoutElement> attributeLayoutElements;
private int[] backwardsCallCounts;
private final CpBands cpBands;
private final AttributeDefinition def;
private boolean usedAtLeastOnce;
private final AttributeLayoutFactory attributeLayoutFactory = new AttributeLayoutFactory();
/**
* Constructs a new NewAttributeBands.
*
* @param effort the effort level.
* @param cpBands the constant pool bands.
* @param header the segment header.
* @param def the attribute definition.
* @throws IOException if an I/O error occurs.
*/
public NewAttributeBands(final int effort, final CpBands cpBands, final SegmentHeader header, final AttributeDefinition def) throws IOException {
super(effort, header);
this.def = def;
this.cpBands = cpBands;
parseLayout();
}
/**
* Adds an attribute to the bands.
*
* @param attribute the attribute to add.
*/
public void addAttribute(final NewAttribute attribute) {
usedAtLeastOnce = true;
final InputStream stream = new ByteArrayInputStream(attribute.getBytes());
for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
attributeLayoutElement.addAttributeToBand(attribute, stream);
}
}
/**
* Gets the attribute name.
*
* @return the attribute name.
*/
public String getAttributeName() {
return def.name.getUnderlyingString();
}
/**
* Returns the {@link BHSDCodec} that should be used for the given layout element
*
* @param layoutElement
*/
private BHSDCodec getCodec(final String layoutElement) {
if (layoutElement.indexOf('O') >= 0) {
return Codec.BRANCH5;
}
if (layoutElement.indexOf('P') >= 0) {
return Codec.BCI5;
}
if (layoutElement.indexOf('S') >= 0 && !layoutElement.contains("KS") //$NON-NLS-1$
&& !layoutElement.contains("RS")) { //$NON-NLS-1$
return Codec.SIGNED5;
}
if (layoutElement.indexOf('B') >= 0) {
return Codec.BYTE1;
}
return Codec.UNSIGNED5;
}
/**
* Gets the flag index.
*
* @return the flag index.
*/
public int getFlagIndex() {
return def.index;
}
/**
* Tests whether this attribute was used at least once.
*
* @return true if used at least once.
*/
public boolean isUsedAtLeastOnce() {
return usedAtLeastOnce;
}
/**
* Gets the number of backwards calls.
*
* @return the backwards call counts.
*/
public int[] numBackwardsCalls() {
return backwardsCallCounts;
}
@Override
public void pack(final OutputStream outputStream) throws IOException, Pack200Exception {
for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
attributeLayoutElement.pack(outputStream);
}
}
private void parseLayout() throws IOException {
final String layout = def.layout.getUnderlyingString();
if (attributeLayoutElements == null) {
attributeLayoutElements = AttributeLayoutUtils.readAttributeLayout(layout, attributeLayoutFactory);
resolveCalls();
}
}
private int readInteger(final int i, final InputStream inputStream) {
int result = 0;
for (int j = 0; j < i; j++) {
try {
result = result << 8 | inputStream.read();
} catch (final IOException e) {
throw new UncheckedIOException("Error reading unknown attribute", e);
}
}
// use casting to preserve sign
if (i == 1) {
result = (byte) result;
}
if (i == 2) {
result = (short) result;
}
return result;
}
/**
* Renumber any bytecode indexes or offsets as described in section 5.5.2 of the pack200 specification
*
* @param bciRenumbering the BCI renumbering list.
* @param labelsToOffsets the map of labels to offsets.
*/
public void renumberBci(final IntList bciRenumbering, final Map<Label, Integer> labelsToOffsets) {
for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
attributeLayoutElement.renumberBci(bciRenumbering, labelsToOffsets);
}
}
/**
* Resolve calls in the attribute layout and returns the number of backwards callables
*/
private void resolveCalls() {
for (int i = 0; i < attributeLayoutElements.size(); i++) {
final AttributeLayoutElement element = attributeLayoutElements.get(i);
if (element instanceof Callable) {
final Callable callable = (Callable) element;
final List<LayoutElement> body = callable.body; // Look for calls in the body
for (final LayoutElement layoutElement : body) {
// Set the callable for each call
resolveCallsForElement(i, callable, layoutElement);
}
}
}
int backwardsCallableIndex = 0;
for (final AttributeLayoutElement attributeLayoutElement : attributeLayoutElements) {
if (attributeLayoutElement instanceof Callable) {
final Callable callable = (Callable) attributeLayoutElement;
if (callable.isBackwardsCallable) {
callable.setBackwardsCallableIndex(backwardsCallableIndex);
backwardsCallableIndex++;
}
}
}
backwardsCallCounts = new int[backwardsCallableIndex];
}
private void resolveCallsForElement(final int i, final Callable currentCallable, final LayoutElement layoutElement) {
if (layoutElement instanceof Call) {
final Call call = (Call) layoutElement;
int index = call.callableIndex;
if (index == 0) { // Calls the parent callable
call.setCallable(currentCallable);
} else if (index > 0) { // Forwards call
for (int k = i + 1; k < attributeLayoutElements.size(); k++) {
final AttributeLayoutElement el = attributeLayoutElements.get(k);
if (el instanceof Callable) {
index--;
if (index == 0) {
call.setCallable((Callable) el);
break;
}
}
}
} else { // Backwards call
for (int k = i - 1; k >= 0; k--) {
final AttributeLayoutElement el = attributeLayoutElements.get(k);
if (el instanceof Callable) {
index++;
if (index == 0) {
call.setCallable((Callable) el);
break;
}
}
}
}
} else if (layoutElement instanceof Replication) {
final List<LayoutElement> children = ((Replication) layoutElement).layoutElements;
for (final LayoutElement child : children) {
resolveCallsForElement(i, currentCallable, child);
}
}
}
}