You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractStructuredAFPObject.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.afp.modca;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.OutputStream;
  22. import java.io.UnsupportedEncodingException;
  23. import java.util.Collection;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import org.apache.commons.io.output.ByteArrayOutputStream;
  27. import org.apache.fop.render.afp.modca.Registry.ObjectType;
  28. import org.apache.fop.render.afp.modca.triplets.FullyQualifiedNameTriplet;
  29. import org.apache.fop.render.afp.modca.triplets.MeasurementUnitsTriplet;
  30. import org.apache.fop.render.afp.modca.triplets.ObjectAreaSizeTriplet;
  31. import org.apache.fop.render.afp.modca.triplets.ObjectClassificationTriplet;
  32. import org.apache.fop.render.afp.modca.triplets.Triplet;
  33. import org.apache.fop.render.afp.tools.BinaryUtils;
  34. /**
  35. * An abstract class encapsulating an MODCA structured object
  36. */
  37. public abstract class AbstractStructuredAFPObject extends AbstractAFPObject {
  38. /**
  39. * list of object triplets
  40. */
  41. protected List/*<Triplet>*/ triplets = null;
  42. /**
  43. * triplet data created from triplet list
  44. */
  45. protected byte[] tripletData = null;
  46. /**
  47. * Default constructor
  48. */
  49. protected AbstractStructuredAFPObject() {
  50. }
  51. /**
  52. * Returns the triplet data length
  53. *
  54. * @return the triplet data length
  55. */
  56. protected int getTripletDataLength() {
  57. if (tripletData == null) {
  58. try {
  59. getTripletData();
  60. } catch (IOException e) {
  61. log.error("failed to get triplet data");
  62. }
  63. }
  64. if (tripletData != null) {
  65. return tripletData.length;
  66. }
  67. return 0;
  68. }
  69. /**
  70. * Returns the triplet data
  71. *
  72. * @return the triplet data
  73. * @throws IOException throws an I/O exception if one occurred
  74. */
  75. protected byte[] getTripletData() throws IOException {
  76. if (tripletData == null && triplets != null) {
  77. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  78. writeObjects(triplets, baos);
  79. this.tripletData = baos.toByteArray();
  80. }
  81. return this.tripletData;
  82. }
  83. /**
  84. * Writes any triplet data
  85. *
  86. * @param os The stream to write to
  87. * @throws IOException The stream to write to
  88. */
  89. protected void writeTriplets(OutputStream os) throws IOException {
  90. if (tripletData != null) {
  91. os.write(tripletData);
  92. } else if (triplets != null) {
  93. writeObjects(triplets, os);
  94. }
  95. }
  96. /**
  97. * Helper method to write the start of the Object.
  98. *
  99. * @param os The stream to write to
  100. * @throws IOException throws an I/O exception if one occurred
  101. */
  102. protected void writeStart(OutputStream os) throws IOException {
  103. getTripletData();
  104. }
  105. /**
  106. * Helper method to write the end of the Object.
  107. *
  108. * @param os The stream to write to
  109. * @throws IOException an I/O exception if one occurred
  110. */
  111. protected void writeEnd(OutputStream os) throws IOException {
  112. }
  113. /**
  114. * Helper method to write the contents of the Object.
  115. *
  116. * @param os The stream to write to
  117. * @throws IOException throws an I/O exception if one occurred
  118. */
  119. protected void writeContent(OutputStream os) throws IOException {
  120. writeTriplets(os);
  121. }
  122. /** {@inheritDoc} */
  123. public void writeToStream(OutputStream os) throws IOException {
  124. writeStart(os);
  125. writeContent(os);
  126. writeEnd(os);
  127. }
  128. /**
  129. * Returns the first matching triplet found in the structured field triplet list
  130. *
  131. * @param tripletId the triplet identifier
  132. */
  133. private Triplet getTriplet(byte tripletId) {
  134. Iterator it = getTriplets().iterator();
  135. while (it.hasNext()) {
  136. Triplet triplet = (Triplet)it.next();
  137. if (triplet.getId() == tripletId) {
  138. return triplet;
  139. }
  140. }
  141. return null;
  142. }
  143. /**
  144. * Returns true of this structured field has the given triplet
  145. *
  146. * @param tripletId the triplet identifier
  147. * @return true if the structured field has the given triplet
  148. */
  149. public boolean hasTriplet(byte tripletId) {
  150. return getTriplet(tripletId) != null;
  151. }
  152. /**
  153. * Adds a triplet to this structured object
  154. *
  155. * @param triplet the triplet to add
  156. */
  157. protected void addTriplet(Triplet triplet) {
  158. getTriplets().add(triplet);
  159. }
  160. /**
  161. * Adds a list of triplets to the triplets contained within this structured field
  162. *
  163. * @param tripletCollection a collection of triplets
  164. */
  165. public void addTriplets(Collection/*<Triplet>*/ tripletCollection) {
  166. if (tripletCollection != null) {
  167. getTriplets().addAll(tripletCollection);
  168. }
  169. }
  170. /** @return the triplet list pertaining to this resource */
  171. protected List/*<Triplet>*/ getTriplets() {
  172. if (triplets == null) {
  173. triplets = new java.util.ArrayList();
  174. }
  175. return triplets;
  176. }
  177. /**
  178. * Sets the fully qualified name of this resource
  179. *
  180. * @param fqnType the fully qualified name type of this resource
  181. * @param fqnFormat the fully qualified name format of this resource
  182. * @param fqName the fully qualified name of this resource
  183. */
  184. public void setFullyQualifiedName(byte fqnType, byte fqnFormat, String fqName) {
  185. addTriplet(new FullyQualifiedNameTriplet(fqnType, fqnFormat, fqName));
  186. }
  187. /** @return the fully qualified name of this triplet or null if it does not exist */
  188. public String getFullyQualifiedName() {
  189. FullyQualifiedNameTriplet fqNameTriplet
  190. = (FullyQualifiedNameTriplet)getTriplet(Triplet.FULLY_QUALIFIED_NAME);
  191. if (fqNameTriplet != null) {
  192. return fqNameTriplet.getFullyQualifiedName();
  193. }
  194. log.warn(this + " has no fully qualified name");
  195. return null;
  196. }
  197. /**
  198. * Sets the objects classification
  199. *
  200. * @param objectClass the classification of the object
  201. * @param objectType the MOD:CA registry object type entry for the given
  202. * object/component type of the object
  203. * @param dataInContainer whether the data resides in the container
  204. * @param containerHasOEG whether the container has an object environment group
  205. * @param dataInOCD whether the data resides in a object container data structured field
  206. */
  207. public void setObjectClassification(
  208. byte objectClass, ObjectType objectType,
  209. boolean dataInContainer, boolean containerHasOEG, boolean dataInOCD) {
  210. addTriplet(
  211. new ObjectClassificationTriplet(
  212. objectClass, objectType, dataInContainer, containerHasOEG, dataInOCD));
  213. }
  214. /**
  215. * Sets the extent of an object area in the X and Y directions
  216. *
  217. * @param x the x direction extent
  218. * @param y the y direction extent
  219. */
  220. public void setObjectAreaSize(int x, int y) {
  221. addTriplet(new ObjectAreaSizeTriplet(x, y));
  222. }
  223. /**
  224. * Sets the measurement units used to specify the units of measure
  225. *
  226. * @param xRes units per base on the x-axis
  227. * @param yRes units per base on the y-axis
  228. */
  229. public void setMeasurementUnits(int xRes, int yRes) {
  230. addTriplet(new MeasurementUnitsTriplet(xRes, xRes));
  231. }
  232. /**
  233. * Sets a comment on this resource
  234. *
  235. * @param comment a comment string
  236. */
  237. public void setComment(String comment) {
  238. try {
  239. addTriplet(new Triplet(Triplet.COMMENT, comment));
  240. } catch (UnsupportedEncodingException e) {
  241. log.error(e.getMessage());
  242. }
  243. }
  244. /**
  245. * Reads data chunks from an inputstream
  246. * and then formats them with a structured header to a given outputstream
  247. *
  248. * @param dataHeader the header data
  249. * @param lengthOffset offset of length field in data chunk
  250. * @param maxChunkLength the maximum chunk length
  251. * @param inputStream the inputstream to read from
  252. * @param outputStream the outputstream to write to
  253. * @throws IOException thrown if an I/O exception of some sort has occurred.
  254. */
  255. protected static void copyChunks(byte[] dataHeader, int lengthOffset,
  256. int maxChunkLength, InputStream inputStream, OutputStream outputStream)
  257. throws IOException {
  258. int headerLen = dataHeader.length - lengthOffset;
  259. // length field is just before data so do not include in data length
  260. if (headerLen == 2) {
  261. headerLen = 0;
  262. }
  263. byte[] data = new byte[maxChunkLength];
  264. int numBytesRead = 0;
  265. while ((numBytesRead = inputStream.read(data, 0, maxChunkLength)) > 0) {
  266. byte[] len = BinaryUtils.convert(headerLen + numBytesRead, 2);
  267. dataHeader[lengthOffset] = len[0]; // Length byte 1
  268. dataHeader[lengthOffset + 1] = len[1]; // Length byte 2
  269. outputStream.write(dataHeader);
  270. outputStream.write(data, 0, numBytesRead);
  271. }
  272. }
  273. /**
  274. * Writes data chunks to a given outputstream
  275. *
  276. * @param data the data byte array
  277. * @param dataHeader the header data
  278. * @param lengthOffset offset of length field in data chunk
  279. * @param maxChunkLength the maximum chunk length
  280. * @param os the outputstream to write to
  281. * @throws IOException thrown if an I/O exception of some sort has occurred.
  282. */
  283. protected static void writeChunksToStream(byte[] data, byte[] dataHeader,
  284. int lengthOffset, int maxChunkLength, OutputStream os) throws IOException {
  285. int dataLength = data.length;
  286. int numFullChunks = dataLength / maxChunkLength;
  287. int lastChunkLength = dataLength % maxChunkLength;
  288. int headerLen = dataHeader.length - lengthOffset;
  289. // length field is just before data so do not include in data length
  290. if (headerLen == 2) {
  291. headerLen = 0;
  292. }
  293. byte[] len;
  294. int off = 0;
  295. if (numFullChunks > 0) {
  296. // write out full data chunks
  297. len = BinaryUtils.convert(headerLen + maxChunkLength, 2);
  298. dataHeader[lengthOffset] = len[0]; // Length byte 1
  299. dataHeader[lengthOffset + 1] = len[1]; // Length byte 2
  300. for (int i = 0; i < numFullChunks; i++, off += maxChunkLength) {
  301. os.write(dataHeader);
  302. os.write(data, off, maxChunkLength);
  303. }
  304. }
  305. if (lastChunkLength > 0) {
  306. // write last data chunk
  307. len = BinaryUtils.convert(headerLen + lastChunkLength, 2);
  308. dataHeader[lengthOffset] = len[0]; // Length byte 1
  309. dataHeader[lengthOffset + 1] = len[1]; // Length byte 2
  310. os.write(dataHeader);
  311. os.write(data, off, lastChunkLength);
  312. }
  313. }
  314. }