public abstract class AbstractAFPObject implements Streamable {
/** Static logging instance */
- protected static final Log LOG = LogFactory.getLog("org.apache.xmlgraphics.afp.modca");
+ protected static final Log LOG = LogFactory.getLog(AbstractAFPObject.class);
/** the structured field class id */
protected static final byte SF_CLASS = (byte)0xD3;
- /** the structure field header */
- static final byte[] SF_HEADER = new byte[] {
+ private static final byte[] SF_HEADER = new byte[] {
0x5A, // Structured field identifier
0x00, // Length byte 1
0x10, // Length byte 2
0x00, // Reserved
};
+ protected static final int SF_HEADER_LENGTH = SF_HEADER.length;
+
/**
* Copies the template structured field data array to the given byte array
*
* @param os The stream to write to
* @throws java.io.IOException an I/O exception of some sort has occurred.
*/
- protected void writeObjects(Collection/*<Streamable>*/ objects, OutputStream os)
- throws IOException {
- if (objects != null && objects.size() > 0) {
- Iterator it = objects.iterator();
+ protected <S extends Streamable> void writeObjects(Collection<S> objects, OutputStream os)
+ throws IOException {
+ if (objects != null) {
+ Iterator<S> it = objects.iterator();
while (it.hasNext()) {
- Object object = it.next();
- if (object instanceof Streamable) {
- ((Streamable)object).writeToStream(os);
- it.remove(); // once written, immediately remove the object
- }
+ Streamable s = it.next();
+ s.writeToStream(os);
+ it.remove(); // once written, immediately remove the object
}
}
}
- /**
- * Reads data chunks from an InputStream
- * and then formats them with a structured header to a given OutputStream
- *
- * @param dataHeader the header data
- * @param lengthOffset offset of length field in data chunk
- * @param maxChunkLength the maximum chunk length
- * @param inputStream the InputStream to read from
- * @param outputStream the OutputStream to write to
- * @throws IOException thrown if an I/O exception of some sort has occurred.
- */
- protected static void copyChunks(byte[] dataHeader, int lengthOffset,
- int maxChunkLength, InputStream inputStream, OutputStream outputStream)
- throws IOException {
- int headerLen = dataHeader.length - lengthOffset;
- // length field is just before data so do not include in data length
- if (headerLen == 2) {
- headerLen = 0;
- }
- byte[] data = new byte[maxChunkLength];
- int numBytesRead = 0;
- while ((numBytesRead = inputStream.read(data, 0, maxChunkLength)) > 0) {
- byte[] len = BinaryUtils.convert(headerLen + numBytesRead, 2);
- dataHeader[lengthOffset] = len[0]; // Length byte 1
- dataHeader[lengthOffset + 1] = len[1]; // Length byte 2
- outputStream.write(dataHeader);
- outputStream.write(data, 0, numBytesRead);
- }
- }
-
/**
* Writes data chunks to a given outputstream
*
* @param maxLength the maximum length allowed for the string
* @return a possibly truncated string
*/
- protected String truncate(String str, int maxLength) {
+ protected static String truncate(String str, int maxLength) {
if (str.length() > maxLength) {
str = str.substring(0, maxLength);
LOG.warn("truncated character string '"
return nameBytes;
}
- /** {@inheritDoc} */
+ @Override
protected void copySF(byte[] data, byte type, byte category) {
super.copySF(data, type, category);
byte[] nameData = getNameBytes();
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
import org.apache.fop.afp.modca.Registry.ObjectType;
/**
* A MODCA structured object base class providing support for Triplets
*/
-public class AbstractTripletStructuredObject extends AbstractStructuredObject {
+public abstract class AbstractTripletStructuredObject extends AbstractStructuredObject {
/** list of object triplets */
- protected List/*<Triplet>*/ triplets = new java.util.ArrayList/*<Triplet>*/();
+ protected List<AbstractTriplet> triplets = new java.util.ArrayList<AbstractTriplet>();
/**
* Returns the triplet data length
*/
protected int getTripletDataLength() {
int dataLength = 0;
- if (hasTriplets()) {
- Iterator it = triplets.iterator();
- while (it.hasNext()) {
- AbstractTriplet triplet = (AbstractTriplet)it.next();
- dataLength += triplet.getDataLength();
- }
+ for (Triplet triplet : triplets) {
+ dataLength += triplet.getDataLength();
}
return dataLength;
}
* @param tripletId the triplet identifier
*/
private AbstractTriplet getTriplet(byte tripletId) {
- Iterator it = getTriplets().iterator();
- while (it.hasNext()) {
- AbstractTriplet triplet = (AbstractTriplet)it.next();
- if (triplet.getId() == tripletId) {
- return triplet;
+ for (AbstractTriplet trip : triplets) {
+ if (trip.getId() == tripletId) {
+ return trip;
}
}
return null;
*
* @param triplet the triplet to add
*/
- protected void addTriplet(Triplet triplet) {
+ protected void addTriplet(AbstractTriplet triplet) {
triplets.add(triplet);
}
*
* @param tripletCollection a collection of triplets
*/
- public void addTriplets(Collection/*<Triplet>*/ tripletCollection) {
+ public void addTriplets(Collection<AbstractTriplet> tripletCollection) {
if (tripletCollection != null) {
triplets.addAll(tripletCollection);
}
}
/** @return the triplet list pertaining to this resource */
- protected List/*<Triplet>*/ getTriplets() {
+ protected List<AbstractTriplet> getTriplets() {
return triplets;
}
/** the object referenced is of type image */
public static final byte TYPE_IMAGE = (byte)0xFB;
-
/** the object type referenced (default is other) */
private byte objectType = TYPE_OTHER;
private int yoaOset = 0;
/** the orientation of the referenced object */
- private int oaOrent = 0;
+ private ObjectAreaRotation oaOrent = ObjectAreaRotation.RIGHT_HANDED_0;
/** the X-axis origin defined in the object */
private int xocaOset = -1;
* The orientation (0,90, 180, 270)
*/
public void setObjectAreaOrientation(int orientation) {
- if (orientation == 0 || orientation == 90 || orientation == 180
- || orientation == 270) {
- this.oaOrent = orientation;
- } else {
- throw new IllegalArgumentException(
- "The orientation must be one of the values 0, 90, 180, 270");
- }
+ this.oaOrent = ObjectAreaRotation.objectAreaRotationFor(orientation);
}
/**
data[17] = 0x00; // reserved
data[18] = objectType;
- //XoaOset (object area)
- if (xoaOset > -1) {
- byte[] x = BinaryUtils.convert(xoaOset, 3);
- data[19] = x[0];
- data[20] = x[1];
- data[21] = x[2];
- } else {
- data[19] = (byte)0xFF;
- data[20] = (byte)0xFF;
- data[21] = (byte)0xFF;
- }
+ writeOsetTo(data, 19, xoaOset);
- // YoaOset (object area)
- if (yoaOset > -1) {
- byte[] y = BinaryUtils.convert(yoaOset, 3);
- data[22] = y[0];
- data[23] = y[1];
- data[24] = y[2];
- } else {
- data[22] = (byte)0xFF;
- data[23] = (byte)0xFF;
- data[24] = (byte)0xFF;
- }
+ writeOsetTo(data, 22, yoaOset);
- // XoaOrent/YoaOrent
- switch (oaOrent) {
- case -1: // use x/y axis orientation defined in object
- data[25] = (byte)0xFF; // x axis rotation
- data[26] = (byte)0xFF; //
- data[27] = (byte)0xFF; // y axis rotation
- data[28] = (byte)0xFF;
- break;
- case 90:
- data[25] = 0x2D;
- data[26] = 0x00;
- data[27] = 0x5A;
- data[28] = 0x00;
- break;
- case 180:
- data[25] = 0x5A;
- data[25] = 0x00;
- data[27] = (byte)0x87;
- data[28] = 0x00;
- break;
- case 270:
- data[25] = (byte)0x87;
- data[26] = 0x00;
- data[27] = 0x00;
- data[28] = 0x00;
- break;
- default: // 0 degrees
- data[25] = 0x00;
- data[26] = 0x00;
- data[27] = 0x2D;
- data[28] = 0x00;
- break;
- }
+ oaOrent.writeTo(data, 25);
- // XocaOset (object content)
- if (xocaOset > -1) {
- byte[] x = BinaryUtils.convert(xocaOset, 3);
- data[29] = x[0];
- data[30] = x[1];
- data[31] = x[2];
- } else {
- data[29] = (byte)0xFF;
- data[30] = (byte)0xFF;
- data[31] = (byte)0xFF;
- }
+ writeOsetTo(data, 29, xocaOset);
+
+ writeOsetTo(data, 32, yocaOset);
- // YocaOset (object content)
- if (yocaOset > -1) {
- byte[] y = BinaryUtils.convert(yocaOset, 3);
- data[32] = y[0];
- data[33] = y[1];
- data[34] = y[2];
- } else {
- data[32] = (byte)0xFF;
- data[33] = (byte)0xFF;
- data[34] = (byte)0xFF;
- }
// RefCSys (Reference coordinate system)
data[35] = 0x01; // Page or overlay coordinate system
writeTriplets(os);
}
+ private static void writeOsetTo(byte[] out, int offset, int oset) {
+ if (oset > -1) {
+ byte[] y = BinaryUtils.convert(oset, 3);
+ out[offset] = y[0];
+ out[offset + 1] = y[1];
+ out[offset + 2] = y[2];
+ } else {
+ out[offset] = (byte)0xFF;
+ out[offset + 1] = (byte)0xFF;
+ out[offset + 2] = (byte)0xFF;
+ }
+ }
+
private String getObjectTypeName() {
String objectTypeName = null;
if (objectType == TYPE_PAGE_SEGMENT) {
addTriplet(new MeasurementUnitsTriplet(xRes, xRes));
}
-}
\ No newline at end of file
+ /**
+ * Represents the 4 bytes that specify the area rotation reference coordinate system
+ *
+ */
+ private enum ObjectAreaRotation {
+
+ RIGHT_HANDED_0(Rotation.ROTATION_0, Rotation.ROTATION_90),
+ RIGHT_HANDED_90(Rotation.ROTATION_90, Rotation.ROTATION_180),
+ RIGHT_HANDED_180(Rotation.ROTATION_180, Rotation.ROTATION_270),
+ RIGHT_HANDED_270(Rotation.ROTATION_270, Rotation.ROTATION_0);
+
+ /**
+ * The object area’s X-axis rotation from the X axis of the reference coordinate system
+ */
+ private final Rotation xoaOrent;
+ /**
+ * The object area’s Y-axis rotation from the Y axis of the reference coordinate system
+ */
+ private final Rotation yoaOrent;
+
+ public void writeTo(byte[] out, int offset) {
+ xoaOrent.writeTo(out, offset);
+ yoaOrent.writeTo(out, offset + 2);
+ }
+
+ ObjectAreaRotation(Rotation xoaOrent, Rotation yoaOrent) {
+ this.xoaOrent = xoaOrent;
+ this.yoaOrent = yoaOrent;
+ }
+
+ private static ObjectAreaRotation objectAreaRotationFor(int orientation) {
+ switch (orientation) {
+ case 0: return RIGHT_HANDED_0;
+ case 90: return RIGHT_HANDED_90;
+ case 180: return RIGHT_HANDED_180;
+ case 270: return RIGHT_HANDED_270;
+ default: throw new IllegalArgumentException(
+ "The orientation must be one of the values 0, 90, 180, 270");
+ }
+ }
+ }
+
+ /**
+ * Represents a rotation value
+ *
+ */
+ private enum Rotation {
+
+ ROTATION_0(0),
+ ROTATION_90(0x2D),
+ ROTATION_180(0x5A),
+ ROTATION_270(0x87);
+
+ private final byte firstByte;
+
+ public void writeTo(byte[] out, int offset) {
+ out[offset] = firstByte;
+ out[offset + 1] = (byte)0;
+ }
+
+ Rotation(int firstByte) {
+ this.firstByte = (byte) firstByte;
+ }
+ }
+
+}
setAttributeValue(value);
setAttributeQualifier(tleID, 1);
- byte[] data = new byte[SF_HEADER.length];
+ byte[] data = new byte[SF_HEADER_LENGTH];
copySF(data, Type.ATTRIBUTE, Category.PROCESS_ELEMENT);
int tripletDataLength = getTripletDataLength();
import org.apache.fop.image.loader.batik.ImageLoaderTestCase;
import org.apache.fop.image.loader.batik.ImagePreloaderTestCase;
import org.apache.fop.intermediate.IFMimickingTestCase;
-import org.apache.fop.render.afp.AFPTestSuite;
import org.apache.fop.render.extensions.prepress.PageBoundariesTest;
import org.apache.fop.render.extensions.prepress.PageScaleTest;
import org.apache.fop.render.pdf.PDFAConformanceTestCase;
//$JUnit-BEGIN$
suite.addTest(BasicDriverTestSuite.suite());
suite.addTest(UtilityCodeTestSuite.suite());
+ suite.addTest(org.apache.fop.afp.AFPTestSuite.suite());
suite.addTest(new TestSuite(PDFAConformanceTestCase.class));
suite.addTest(new TestSuite(PDFEncodingTestCase.class));
suite.addTest(new TestSuite(PDFCMapTestCase.class));
suite.addTest(new TestSuite(PDFsRGBSettingsTestCase.class));
suite.addTest(new TestSuite(DejaVuLGCSerifTest.class));
suite.addTest(new TestSuite(MODCAParserTestCase.class));
- suite.addTest(AFPTestSuite.suite());
+ suite.addTest(org.apache.fop.render.afp.AFPTestSuite.suite());
suite.addTest(PSTestSuite.suite());
suite.addTest(RichTextFormatTestSuite.suite());
suite.addTest(new TestSuite(ImageLoaderTestCase.class));
--- /dev/null
+/*
+ * 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
+ *
+ * http://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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.apache.fop.afp.modca.AbstractStructuredObjectTestCase;
+import org.apache.fop.afp.modca.AbstractTripletStructuredObjectTestCase;
+import org.apache.fop.afp.modca.IncludeObjectTestCase;
+
+/**
+ * Test suite for FOP's AFP classes.
+ */
+public class AFPTestSuite {
+ /**
+ * Builds the test suite
+ * @return the test suite
+ */
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Test suite for FOP's AFP classes");
+ //$JUnit-BEGIN$
+ suite.addTest(new TestSuite(IncludeObjectTestCase.class));
+ //$JUnit-END$
+ return suite;
+ }
+}
--- /dev/null
+/*
+ * 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
+ *
+ * http://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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.fop.afp.Streamable;
+
+/**
+ * Tests the {@link AbstractAFPObject} class.
+ */
+public abstract class AbstractAFPObjectTestCase<S extends AbstractAFPObject>
+ extends TestCase {
+
+ private S sut;
+
+ protected final S getSut() {
+ return sut;
+ }
+
+ protected final void setSut(S sut) {
+ if ( this.sut == null) {
+ this.sut = sut;
+ }
+ }
+
+
+ private byte[] header = new byte[] {
+ 0x5A, // Structured field identifier
+ 0x00, // Length byte 1
+ 0x10, // Length byte 2
+ 0x00, // Structured field id byte 1
+ 0x00, // Structured field id byte 2
+ 0x00, // Structured field id byte 3
+ 0x00, // Flags
+ 0x00, // Reserved
+ 0x00 // Reserved
+ };
+
+
+ public void testCopySFStatic() {
+ byte[] actual = new byte[9];
+ Arrays.fill(actual, (byte)-1);
+
+ S.copySF(actual, (byte)0, (byte)0, (byte)0);
+
+ assertTrue(Arrays.equals(actual, header));
+
+ byte[] expected2 = new byte[9];
+ System.arraycopy(header, 0, expected2, 0, header.length);
+
+ final byte clazz = (byte) 0x01;
+ final byte type = (byte) 0x02;
+ final byte catagory = (byte) 0x03;
+ expected2[3] = clazz;
+ expected2[4] = type;
+ expected2[5] = catagory;
+
+ AbstractAFPObject.copySF(actual, clazz, type, catagory);
+
+ assertTrue(Arrays.equals(actual, expected2));
+ }
+
+ public void testCopySF() {
+ byte[] expected = new byte[9];
+ S.copySF(expected, (byte) 0xD3, (byte)0, (byte)0);
+
+ byte[] actual = new byte[9];
+ Arrays.fill(actual, (byte)-1);
+
+ getSut().copySF(actual, (byte)0, (byte)0);
+
+ assertTrue(Arrays.equals(actual, expected));
+
+ byte[] expected2 = new byte[9];
+ System.arraycopy(expected, 0, expected2, 0, expected.length);
+
+ final byte type = (byte)1;
+ final byte catagory = (byte)2;
+ expected2[4] = type;
+ expected2[5] = catagory;
+
+ getSut().copySF(actual, type, catagory);
+
+ assertTrue(Arrays.equals(actual, expected2));
+ }
+
+ /**
+ *
+ */
+ public void testwriteObjects() {
+ final byte[][] expected = {{(byte)0, (byte)1}, {(byte)2, (byte)3}, {(byte)4, (byte)5}};
+
+ List<Streamable> objects = new ArrayList<Streamable>() {
+ {
+ add(StreamableObject.instance(expected[0]));
+ add(StreamableObject.instance(expected[1]));
+ add(StreamableObject.instance(expected[2]));
+ } };
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ try {
+ getSut().writeObjects(objects, baos);
+ } catch (IOException e) {
+ fail();
+ }
+
+ byte[] actual = baos.toByteArray();
+
+ int index = 0;
+ for (int i = 0; i < expected.length; i++) {
+ for (int j = 0; j < expected[i].length; j++) {
+ assertTrue("" + index, actual[index] == expected[i][j]);
+ index++;
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ public void testTruncate() {
+ String expected = "abc";
+ assertTrue(AbstractAFPObject.truncate(expected, 4) == expected);
+ assertTrue(AbstractAFPObject.truncate(expected, 3) == expected);
+ assertEquals(AbstractAFPObject.truncate(expected + "d", 3), expected);
+ assertEquals(AbstractAFPObject.truncate(expected, 0), "");
+ try {
+ assertTrue(AbstractAFPObject.truncate(null, 4) == null);
+ fail();
+ } catch (NullPointerException e) {
+ // PASS
+ }
+ }
+
+ /**
+ *
+ */
+ public void testWriteChunksToStream() throws IOException {
+ final byte[] data = new byte[256];
+ int counter = 0;
+ for (int i = 0; i < data.length; i++) {
+ data[i] = (byte) counter++;
+ }
+
+ byte[] header = new byte[9];
+ // Test when chunk size % data.length == 0
+ testWithGivenChunkSize(data, header, 16);
+
+ // test when chunk size % data.length != 0
+ testWithGivenChunkSize(data, header, 10);
+
+ // test with an odd number...
+ testWithGivenChunkSize(data, header, 13);
+ }
+
+ private void testWithGivenChunkSize(byte[] data, byte[] header, int chunkSize)
+ throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ S.writeChunksToStream(data, header, 0, chunkSize, baos);
+ byte[] testData = baos.toByteArray();
+
+ int numberOfFullDataChunks = data.length / chunkSize;
+ int lastChunkSize = data.length % chunkSize;
+ int lengthOfTestData = numberOfFullDataChunks * (chunkSize + header.length);
+ lengthOfTestData += lastChunkSize == 0 ? 0 : header.length + lastChunkSize;
+
+ putLengthInHeader(header, chunkSize);
+
+ assertEquals(lengthOfTestData, testData.length);
+ int testIndex = 0;
+ int expectedIndex = 0;
+ for (int i = 0; i < numberOfFullDataChunks; i++) {
+ checkHeaderAndData(header, data, testData, expectedIndex, testIndex, chunkSize);
+ expectedIndex += chunkSize + header.length;
+ testIndex += chunkSize;
+ }
+
+ putLengthInHeader(header, lastChunkSize);
+ // check last chunk
+ if (lastChunkSize != 0) {
+ checkHeaderAndData(header, data, testData, expectedIndex, testIndex, lastChunkSize);
+ }
+ }
+
+ private void putLengthInHeader(byte[] header, int chunkSize) {
+ header[0] = 0;
+ header[1] = (byte) (chunkSize + header.length);
+ }
+
+ private void checkHeaderAndData(byte[] header, byte[] data, byte[] testData, int expectedIndex,
+ int testIndex, int chunkSize) {
+ for (int i = 0; i < header.length; i++) {
+ assertEquals(testData[expectedIndex++], header[i]);
+ }
+ for (int i = 0; i < chunkSize; i++) {
+ assertEquals(testData[expectedIndex++], data[i + testIndex]);
+ }
+ }
+
+ /**
+ *
+ */
+ private static class StreamableObject implements Streamable {
+ private byte[] bytes;
+
+ StreamableObject(byte[] bytes) {
+ this.bytes = new byte[bytes.length];
+ System.arraycopy(bytes, 0, this.bytes, 0, bytes.length);
+ }
+
+ private static Streamable instance(byte[] bytes) {
+ return new StreamableObject(bytes);
+ }
+
+ public void writeToStream(OutputStream os) throws IOException {
+ os.write(bytes);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
+ *
+ * http://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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.modca;
+
+import java.util.Arrays;
+
+/**
+ * Tests the {@linkplain AbstractAFPObject} class.
+ */
+public abstract class AbstractNamedAFPObjectTestCase<S extends AbstractNamedAFPObject>
+ extends AbstractAFPObjectTestCase<S> {
+
+ public void testCopySF() {
+
+ final S sut = getSut();
+
+ byte[] expected = new byte[17];
+ S.copySF(expected, (byte) 0xD3, (byte)0, (byte)0);
+
+ byte[] nameData = sut.getNameBytes();
+ System.arraycopy(nameData, 0, expected, 9, nameData.length);
+
+ byte[] actual = new byte[17];
+ Arrays.fill(actual, (byte)-1);
+
+ getSut().copySF(actual, (byte)0, (byte)0);
+
+ assertTrue(Arrays.equals(actual, expected));
+
+ byte[] expected2 = new byte[17];
+ System.arraycopy(expected, 0, expected2, 0, expected.length);
+ System.arraycopy(nameData, 0, expected, 9, nameData.length);
+
+ final byte type = (byte)1;
+ final byte catagory = (byte)2;
+ expected2[4] = type;
+ expected2[5] = catagory;
+
+ getSut().copySF(actual, type, catagory);
+
+ assertTrue(Arrays.equals(actual, expected2));
+ }
+}
--- /dev/null
+/*
+ * 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
+ *
+ * http://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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.modca;
+
+import java.io.IOException;
+
+public abstract class AbstractStructuredObjectTestCase<S extends AbstractStructuredObject> extends AbstractAFPObjectTestCase<S> {
+
+ /**
+ * Test writeStart() - test that the contract is maintained with
+ * {@link AbstractStructuredObject}.
+ *
+ * @throws IOException
+ */
+ public void testwriteStart() throws IOException {
+ }
+
+ /**
+ * Test writeEnd() - test that the contract is maintained with {@link AbstractStructuredObject}.
+ *
+ * @throws IOException
+ */
+ public void testWriteEnd() throws IOException {
+ }
+
+ /**
+ * Test writeContent() - test that the contract is maintained with
+ * {@link AbstractStructuredObject}.
+ *
+ * @throws IOException
+ */
+ public void testWriteContent() throws IOException {
+ }
+
+ /**
+ * Test writeToStream() - test that the contract is maintained with
+ * {@link AbstractStructuredObject}.
+ *
+ * @throws IOException
+ */
+ public void testWriteToStream() throws IOException {
+ testwriteStart();
+ testWriteEnd();
+ testWriteContent();
+ }
+}
--- /dev/null
+/*
+ * 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
+ *
+ * http://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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.fop.afp.modca.triplets.AbstractTriplet;
+import org.apache.fop.afp.modca.triplets.AttributeQualifierTriplet;
+import org.apache.fop.afp.modca.triplets.CommentTriplet;
+import org.apache.fop.afp.modca.triplets.ObjectAreaSizeTriplet;
+import org.apache.fop.afp.modca.triplets.Triplet;
+
+/**
+ * Test {@link AbstractTripletStructuredObject}
+ */
+public abstract class AbstractTripletStructuredObjectTestCase<S extends AbstractTripletStructuredObject>
+ extends AbstractStructuredObjectTestCase<AbstractTripletStructuredObject> {
+
+ private static final List<AbstractTriplet> TRIPLETS;
+
+ static {
+ List<AbstractTriplet> triplets = new ArrayList<AbstractTriplet>();
+
+ triplets.add(new CommentTriplet((byte) 0x01, "test comment"));
+
+ triplets.add(new AttributeQualifierTriplet(1, 1));
+
+ triplets.add(new ObjectAreaSizeTriplet(10, 20));
+
+ TRIPLETS = Collections.unmodifiableList(triplets);
+ }
+
+ private AbstractTripletStructuredObject emptyStructuredObject
+ = new AbstractTripletStructuredObject() { };
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ AbstractTripletStructuredObject sut = getSut();
+
+ for (AbstractTriplet triplet : TRIPLETS) {
+ sut.addTriplet(triplet);
+ }
+ }
+
+
+ /**
+ * Test getTripletLength() - ensure a sum of all enclosing object lengths is returned.
+ */
+ public void testGetTripletLength() {
+
+ int dataLength = 0;
+ for (Triplet t : TRIPLETS) {
+ dataLength += t.getDataLength();
+ }
+ assertEquals(dataLength, getSut().getTripletDataLength());
+ assertEquals(0, emptyStructuredObject.getTripletDataLength());
+ }
+
+ /**
+ * Test hasTriplets()
+ */
+ public void testHasTriplets() {
+ assertTrue(getSut().hasTriplets());
+ assertFalse(emptyStructuredObject.hasTriplets());
+ }
+
+ /**
+ * Test writeTriplets() - Ensure the triplets are written properly.
+ *
+ * @throws IOException -
+ */
+ public void testWriteObjects() throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (AbstractTriplet triplet : TRIPLETS) {
+ triplet.writeToStream(baos);
+ }
+ byte[] expected = baos.toByteArray();
+ baos.reset();
+ getSut().writeTriplets(baos);
+ assertTrue(Arrays.equals(expected, baos.toByteArray()));
+
+ baos.reset();
+ // Ensure it doesn't die if no data has been added
+ emptyStructuredObject.writeTriplets(baos);
+ byte[] emptyArray = baos.toByteArray();
+ assertTrue(Arrays.equals(emptyArray, new byte[0]));
+ }
+
+ /**
+ * Test hasTriplet() - ensure both positive and negative values are returned.
+ */
+ public void testHasTriplet() {
+ for (AbstractTriplet triplet : TRIPLETS) {
+ assertTrue(getSut().hasTriplet(triplet.getId()));
+ assertFalse(emptyStructuredObject.hasTriplet(triplet.getId()));
+ }
+ CommentTriplet notInSystem = new CommentTriplet((byte) 0x30, "This should return false");
+ assertFalse(getSut().hasTriplet(notInSystem.getId()));
+ }
+
+ /**
+ * Test addTriplet() - mostly tested above, but check boundary cases
+ */
+ public void testAddTriplet() {
+ // ensure null doesn't kill it... not sure what else to test
+ getSut().addTriplet(null);
+ }
+
+ /**
+ * Test addTriplets() - ensure all triplets are added.
+ */
+ public void testAddTriplets() {
+ // Tested on empty object
+ List<AbstractTriplet> expectedList = TRIPLETS;
+ emptyStructuredObject.addTriplets(expectedList);
+ // checks equals() on each member of both lists
+ assertEquals(expectedList, emptyStructuredObject.getTriplets());
+
+ // Add a list to an already populated list
+ getSut().addTriplets(expectedList);
+
+ List<AbstractTriplet> newExpected = new ArrayList<AbstractTriplet>(expectedList);
+ newExpected.addAll(expectedList);
+ assertEquals(newExpected, getSut().getTriplets());
+
+ // Ensure null doesn't throw exception
+ emptyStructuredObject.addTriplets(null);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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
+ *
+ * http://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.
+ */
+
+/* $Id:$ */
+
+package org.apache.fop.afp.modca;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.fop.afp.util.BinaryUtils;
+
+/**
+ * Test {@link IncludeObject}
+ */
+public class IncludeObjectTestCase extends AbstractNamedAFPObjectTestCase<IncludeObject> {
+
+ @Override
+ public void setUp() throws Exception {
+ setSut(new IncludeObject("8__chars"));
+ super.setUp();
+ }
+
+ /**
+ * Test writeToStream()
+ * @throws IOException -
+ */
+ public void testWriteToStream() throws IOException {
+ final IncludeObject sut = getSut();
+
+ byte[] expected = defaultIncludeObjectBytes(sut.getTripletDataLength(), sut.getNameBytes());
+
+ testWriteToStreamHelper(sut, expected);
+ }
+
+ /**
+ * Test writeToStream() - the orientation of the referenced object is a right-
+ * handed with a 180 x-axis
+ * @throws IOException -
+ */
+ public void testWriteToStreamForOrientation() throws IOException {
+ final IncludeObject sut = getSut();
+
+ byte[] expected = defaultIncludeObjectBytes(sut.getTripletDataLength(), sut.getNameBytes());
+
+ expected[25] = (byte)0x5A;
+ expected[26] = (byte)0x00;
+ expected[27] = (byte)0x87;
+ expected[28] = (byte)0x00;
+
+ sut.setObjectAreaOrientation(180);
+
+ testWriteToStreamHelper(sut, expected);
+ }
+
+ private void testWriteToStreamHelper(IncludeObject sut, byte[] expected) throws IOException {
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ sut.writeToStream(baos);
+
+ byte[] actual = baos.toByteArray();
+
+ assertTrue(Arrays.equals(actual, expected));
+ }
+
+ private byte[] defaultIncludeObjectBytes(int tripletDataLength, byte[] nameData) {
+
+ byte[] expected = new byte[36];
+
+ byte[] header = new byte[] {
+ 0x5A, // Structured field identifier
+ 0x00, // Length byte 1
+ 0x10, // Length byte 2
+ (byte)0xD3, // Structured field id byte 1
+ (byte)0xAF, // Structured field id byte 2 - type 'input'
+ (byte)0xC3, // Structured field id byte 3 - category 'data resource'
+ 0x00, // Flags
+ 0x00, // Reserved
+ 0x00, // Reserved
+ };
+
+ System.arraycopy(header, 0, expected, 0, header.length);
+
+ byte[] lengthBytes = BinaryUtils.convert(35 + tripletDataLength, 2); //Ignore first byte
+ expected[1] = lengthBytes[0];
+ expected[2] = lengthBytes[1];
+
+ System.arraycopy(nameData, 0, expected, 9, nameData.length);
+
+ expected[18] = (byte)0x92; // object type 'other'
+
+ expected[27] = (byte)0x2D; // orientation of the reference object
+ writeOsetTo(expected, 29, -1); // the X-axis origin defined in the object
+ writeOsetTo(expected, 32, -1); // the Y-axis origin defined in the object
+
+ expected[35] = 0x01; // Page or overlay coordinate system
+
+ return expected;
+ }
+
+ private static void writeOsetTo(byte[] out, int offset, int oset) {
+ if (oset > -1) {
+ byte[] y = BinaryUtils.convert(oset, 3);
+ out[offset] = y[0];
+ out[offset + 1] = y[1];
+ out[offset + 2] = y[2];
+ } else {
+ out[offset] = (byte)0xFF;
+ out[offset + 1] = (byte)0xFF;
+ out[offset + 2] = (byte)0xFF;
+ }
+ }
+}
\ No newline at end of file