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.

RecordContainer.java 10KB

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. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hslf.record;
  16. import org.apache.poi.util.ArrayUtil;
  17. import org.apache.poi.util.LittleEndian;
  18. import org.apache.poi.hslf.util.MutableByteArrayOutputStream;
  19. import java.io.IOException;
  20. import java.io.OutputStream;
  21. import java.io.ByteArrayOutputStream;
  22. import java.util.ArrayList;
  23. /**
  24. * Abstract class which all container records will extend. Providers
  25. * helpful methods for writing child records out to disk
  26. *
  27. * @author Nick Burch
  28. */
  29. public abstract class RecordContainer extends Record
  30. {
  31. protected Record[] _children;
  32. /**
  33. * Return any children
  34. */
  35. public Record[] getChildRecords() { return _children; }
  36. /**
  37. * We're not an atom
  38. */
  39. public boolean isAnAtom() { return false; }
  40. /* ===============================================================
  41. * Internal Move Helpers
  42. * ===============================================================
  43. */
  44. /**
  45. * Finds the location of the given child record
  46. */
  47. private int findChildLocation(Record child) {
  48. int i=0;
  49. for(Record r : _children) {
  50. if (r.equals(child)) {
  51. return i;
  52. }
  53. i++;
  54. }
  55. return -1;
  56. }
  57. /**
  58. * Adds a child record, at the very end.
  59. * @param newChild The child record to add
  60. */
  61. private void appendChild(Record newChild) {
  62. // Copy over, and pop the child in at the end
  63. Record[] nc = new Record[(_children.length + 1)];
  64. System.arraycopy(_children, 0, nc, 0, _children.length);
  65. // Switch the arrays
  66. nc[_children.length] = newChild;
  67. _children = nc;
  68. }
  69. /**
  70. * Adds the given new Child Record at the given location,
  71. * shuffling everything from there on down by one
  72. * @param newChild
  73. * @param position
  74. */
  75. private void addChildAt(Record newChild, int position) {
  76. // Firstly, have the child added in at the end
  77. appendChild(newChild);
  78. // Now, have them moved to the right place
  79. moveChildRecords( (_children.length-1), position, 1 );
  80. }
  81. /**
  82. * Moves {@code number} child records from {@code oldLoc} to {@code newLoc}.
  83. * @param oldLoc the current location of the records to move
  84. * @param newLoc the new location for the records
  85. * @param number the number of records to move
  86. */
  87. private void moveChildRecords(int oldLoc, int newLoc, int number) {
  88. if(oldLoc == newLoc) { return; }
  89. if(number == 0) { return; }
  90. // Check that we're not asked to move too many
  91. if(oldLoc+number > _children.length) {
  92. throw new IllegalArgumentException("Asked to move more records than there are!");
  93. }
  94. // Do the move
  95. ArrayUtil.arrayMoveWithin(_children, oldLoc, newLoc, number);
  96. }
  97. /**
  98. * Finds the first child record of the given type,
  99. * or null if none of the child records are of the
  100. * given type. Does not descend.
  101. */
  102. public Record findFirstOfType(long type) {
  103. for (Record r : _children) {
  104. if (r.getRecordType() == type) {
  105. return r;
  106. }
  107. }
  108. return null;
  109. }
  110. /**
  111. * Remove a child record from this record container
  112. *
  113. * @param ch the child to remove
  114. * @return the removed record
  115. */
  116. public Record removeChild(Record ch) {
  117. Record rm = null;
  118. ArrayList<Record> lst = new ArrayList<Record>();
  119. for(Record r : _children) {
  120. if(r != ch) lst.add(r);
  121. else rm = r;
  122. }
  123. _children = lst.toArray(new Record[lst.size()]);
  124. return rm;
  125. }
  126. /* ===============================================================
  127. * External Move Methods
  128. * ===============================================================
  129. */
  130. /**
  131. * Add a new child record onto a record's list of children.
  132. */
  133. public void appendChildRecord(Record newChild) {
  134. appendChild(newChild);
  135. }
  136. /**
  137. * Adds the given Child Record after the supplied record
  138. * @param newChild
  139. * @param after
  140. */
  141. public void addChildAfter(Record newChild, Record after) {
  142. // Decide where we're going to put it
  143. int loc = findChildLocation(after);
  144. if(loc == -1) {
  145. throw new IllegalArgumentException("Asked to add a new child after another record, but that record wasn't one of our children!");
  146. }
  147. // Add one place after the supplied record
  148. addChildAt(newChild, loc+1);
  149. }
  150. /**
  151. * Adds the given Child Record before the supplied record
  152. * @param newChild
  153. * @param before
  154. */
  155. public void addChildBefore(Record newChild, Record before) {
  156. // Decide where we're going to put it
  157. int loc = findChildLocation(before);
  158. if(loc == -1) {
  159. throw new IllegalArgumentException("Asked to add a new child before another record, but that record wasn't one of our children!");
  160. }
  161. // Add at the place of the supplied record
  162. addChildAt(newChild, loc);
  163. }
  164. /**
  165. * Moves the given Child Record to before the supplied record
  166. */
  167. public void moveChildBefore(Record child, Record before) {
  168. moveChildrenBefore(child, 1, before);
  169. }
  170. /**
  171. * Moves the given Child Records to before the supplied record
  172. */
  173. public void moveChildrenBefore(Record firstChild, int number, Record before) {
  174. if(number < 1) { return; }
  175. // Decide where we're going to put them
  176. int newLoc = findChildLocation(before);
  177. if(newLoc == -1) {
  178. throw new IllegalArgumentException("Asked to move children before another record, but that record wasn't one of our children!");
  179. }
  180. // Figure out where they are now
  181. int oldLoc = findChildLocation(firstChild);
  182. if(oldLoc == -1) {
  183. throw new IllegalArgumentException("Asked to move a record that wasn't a child!");
  184. }
  185. // Actually move
  186. moveChildRecords(oldLoc, newLoc, number);
  187. }
  188. /**
  189. * Moves the given Child Records to after the supplied record
  190. */
  191. public void moveChildrenAfter(Record firstChild, int number, Record after) {
  192. if(number < 1) { return; }
  193. // Decide where we're going to put them
  194. int newLoc = findChildLocation(after);
  195. if(newLoc == -1) {
  196. throw new IllegalArgumentException("Asked to move children before another record, but that record wasn't one of our children!");
  197. }
  198. // We actually want after this though
  199. newLoc++;
  200. // Figure out where they are now
  201. int oldLoc = findChildLocation(firstChild);
  202. if(oldLoc == -1) {
  203. throw new IllegalArgumentException("Asked to move a record that wasn't a child!");
  204. }
  205. // Actually move
  206. moveChildRecords(oldLoc, newLoc, number);
  207. }
  208. /**
  209. * Set child records.
  210. *
  211. * @param records the new child records
  212. */
  213. public void setChildRecord(Record[] records) {
  214. this._children = records.clone();
  215. }
  216. /* ===============================================================
  217. * External Serialisation Methods
  218. * ===============================================================
  219. */
  220. /**
  221. * Write out our header, and our children.
  222. * @param headerA the first byte of the header
  223. * @param headerB the second byte of the header
  224. * @param type the record type
  225. * @param children our child records
  226. * @param out the stream to write to
  227. */
  228. public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException {
  229. // If we have a mutable output stream, take advantage of that
  230. if(out instanceof MutableByteArrayOutputStream) {
  231. MutableByteArrayOutputStream mout =
  232. (MutableByteArrayOutputStream)out;
  233. // Grab current size
  234. int oldSize = mout.getBytesWritten();
  235. // Write out our header, less the size
  236. mout.write(new byte[] {headerA,headerB});
  237. byte[] typeB = new byte[2];
  238. LittleEndian.putShort(typeB, 0, (short)type);
  239. mout.write(typeB);
  240. mout.write(new byte[4]);
  241. // Write out the children
  242. for(int i=0; i<children.length; i++) {
  243. children[i].writeOut(mout);
  244. }
  245. // Update our header with the size
  246. // Don't forget to knock 8 more off, since we don't include the
  247. // header in the size
  248. int length = mout.getBytesWritten() - oldSize - 8;
  249. byte[] size = new byte[4];
  250. LittleEndian.putInt(size,0,length);
  251. mout.overwrite(size, oldSize+4);
  252. } else {
  253. // Going to have to do it a slower way, because we have
  254. // to update the length come the end
  255. // Create a ByteArrayOutputStream to hold everything in
  256. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  257. // Write out our header, less the size
  258. baos.write(new byte[] {headerA,headerB});
  259. byte[] typeB = new byte[2];
  260. LittleEndian.putShort(typeB,0,(short)type);
  261. baos.write(typeB);
  262. baos.write(new byte[] {0,0,0,0});
  263. // Write out our children
  264. for(int i=0; i<children.length; i++) {
  265. children[i].writeOut(baos);
  266. }
  267. // Grab the bytes back
  268. byte[] toWrite = baos.toByteArray();
  269. // Update our header with the size
  270. // Don't forget to knock 8 more off, since we don't include the
  271. // header in the size
  272. LittleEndian.putInt(toWrite,4,(toWrite.length-8));
  273. // Write out the bytes
  274. out.write(toWrite);
  275. }
  276. }
  277. /**
  278. * Find the records that are parent-aware, and tell them who their parent is
  279. */
  280. public static void handleParentAwareRecords(RecordContainer br) {
  281. // Loop over child records, looking for interesting ones
  282. for (Record record : br.getChildRecords()) {
  283. // Tell parent aware records of their parent
  284. if (record instanceof ParentAwareRecord) {
  285. ((ParentAwareRecord) record).setParentRecord(br);
  286. }
  287. // Walk on down for the case of container records
  288. if (record instanceof RecordContainer) {
  289. handleParentAwareRecords((RecordContainer)record);
  290. }
  291. }
  292. }
  293. }