package com.grendelscan.commons.flex.arrays; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import org.apache.commons.lang.NotImplementedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.grendelscan.commons.StringUtils; import com.grendelscan.commons.flex.AbstractAmfData; import com.grendelscan.commons.flex.AbstractAmfDataContainer; import com.grendelscan.commons.flex.AmfOutputStream; import com.grendelscan.commons.flex.AmfUtils; import com.grendelscan.commons.flex.dataTypeDefinitions.AmfDataType; import com.grendelscan.commons.flex.interfaces.Orderable; import com.grendelscan.commons.flex.output.AmfOutputStreamRegistry; import com.grendelscan.commons.http.dataHandling.containers.ExpandableDataContainer; import com.grendelscan.commons.http.dataHandling.data.Data; import com.grendelscan.commons.http.dataHandling.references.DataReference; import com.grendelscan.commons.http.dataHandling.references.NumberedListDataReference; public abstract class AmfPrimitiveArray extends AbstractAmfDataContainer<NumberedListDataReference> implements Orderable, ExpandableDataContainer<NumberedListDataReference> { private static final Logger LOGGER = LoggerFactory.getLogger(AmfPrimitiveArray.class); private static final long serialVersionUID = 1L; private final ArrayList<AbstractAmfData> data; public AmfPrimitiveArray(final String name, final AmfDataType type, final AbstractAmfDataContainer<?> parent, final int transactionId) { super(name, type, parent, false, transactionId); data = new ArrayList<AbstractAmfData>(1); } @Override public void addChild(final AbstractAmfData value) { data.add(value); value.setDeletable(true); renumberChildren(); } @Override public void addChild(@SuppressWarnings("unused") final Data child) { throw new NotImplementedException("Can only have AMF children"); } @Override public void addChild(final int index, final AbstractAmfData value) { data.add(index, value); value.setDeletable(true); renumberChildren(); } @Override public void addChild(final NumberedListDataReference reference, final Data child) { data.add(reference.getIndex(), (AbstractAmfData) child); } /* * (non-Javadoc) * * @see com.grendelscan.commons.http.dataHandling.containers.DataContainer#childrenDebugString() */ @Override public String childrenDebugString() { StringBuilder sb = new StringBuilder(); int i = 0; for (AbstractAmfData child : getChildren()) { sb.append("\n\t["); sb.append(i++); sb.append("]\n"); sb.append(StringUtils.indentLines(child.debugString(), 1)); } return sb.toString(); } public AbstractAmfData get(final int index) { return data.get(index); } @Override public Data getChild(final NumberedListDataReference reference) { return data.get(reference.getIndex()); } @Override public ArrayList<AbstractAmfData> getChildren() { return data; } @Override public NumberedListDataReference getChildsReference(final Data child) { int index = data.indexOf(child); if (index < 0) { throw new IllegalArgumentException("The passed data object is not a child of this container"); } return new NumberedListDataReference(index); } @Override abstract public AmfDataType[] getChildTypes(); @Override public Data getChildUnsafeType(final DataReference reference) { return getChild((NumberedListDataReference) reference); } @Override public Data[] getDataChildren() { return data.toArray(new Data[data.size()]); } public int getSize() { return data.size(); } @Override public boolean isMutable() { return true; } @Override public boolean isValueLocked() { return true; } public void remove(final AbstractAmfData child) { data.remove(child); } @Override public void removeChild(final Data child) { data.remove(child); } @Override public void removeChild(final int index) { data.remove(index); renumberChildren(); } public void renumberChildren() { for (int i = 0; i < data.size(); i++) { AbstractAmfData datum = data.get(i); datum.setName(getName() + "[" + i + "]"); } } @Override public void replaceChild(final NumberedListDataReference reference, final Data child) { data.set(reference.getIndex(), (AbstractAmfData) child); } @Override public void setName(final String name) { super.setName(name); renumberChildren(); } protected void writeArrayHeaderToStream(final AmfOutputStream outputStream) { try { writeCodeToStream(outputStream); if (useAmf3Code) { // Write out an invalid reference, storing the length in the unused // 28-bits. AmfUtils.writeUInt29(outputStream, getSize() << 1 | 1); // Send an empty string to imply no named keys AmfUtils.writeAMFUTF(outputStream, false, "", useAmf3Code); } else { outputStream.writeInt(getSize()); } } catch (IOException e) { LOGGER.error("Problem writing AMF array header: " + e.toString(), e); } } @Override public void writeBytes(final OutputStream out) { AmfOutputStream outputStream = AmfOutputStreamRegistry.getStream(out); writeArrayHeaderToStream(outputStream); for (Data d : data) { AbstractAmfData datum = (AbstractAmfData) d; datum.writeBytes(outputStream); } } }