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 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 java.io.IOException;
  17. import java.io.OutputStream;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Map;
  21. import java.util.function.Supplier;
  22. import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
  23. import org.apache.poi.util.ArrayUtil;
  24. import org.apache.poi.util.LittleEndian;
  25. /**
  26. * Abstract class which all container records will extend. Providers
  27. * helpful methods for writing child records out to disk
  28. */
  29. public abstract class RecordContainer extends Record
  30. {
  31. protected Record[] _children;
  32. /**
  33. * Return any children
  34. */
  35. @Override
  36. public org.apache.poi.hslf.record.Record[] getChildRecords() { return _children; }
  37. /**
  38. * We're not an atom
  39. */
  40. @Override
  41. public boolean isAnAtom() { return false; }
  42. /* ===============================================================
  43. * Internal Move Helpers
  44. * ===============================================================
  45. */
  46. /**
  47. * Finds the location of the given child record
  48. */
  49. private int findChildLocation(Record child) {
  50. int i=0;
  51. for(org.apache.poi.hslf.record.Record r : _children) {
  52. if (r.equals(child)) {
  53. return i;
  54. }
  55. i++;
  56. }
  57. return -1;
  58. }
  59. /**
  60. * Adds a child record, at the very end.
  61. * @param newChild The child record to add
  62. * @return the position of the added child
  63. */
  64. private int appendChild(Record newChild) {
  65. // Copy over, and pop the child in at the end
  66. Record[] nc = Arrays.copyOf(_children, _children.length+1, org.apache.poi.hslf.record.Record[].class);
  67. // Switch the arrays
  68. nc[_children.length] = newChild;
  69. _children = nc;
  70. return _children.length;
  71. }
  72. /**
  73. * Adds the given new Child Record at the given location,
  74. * shuffling everything from there on down by one
  75. *
  76. * @param newChild The record to be added as child-record.
  77. * @param position The index where the child should be added, 0-based
  78. */
  79. private void addChildAt(Record newChild, int position) {
  80. // Firstly, have the child added in at the end
  81. appendChild(newChild);
  82. // Now, have them moved to the right place
  83. moveChildRecords( (_children.length-1), position, 1 );
  84. }
  85. /**
  86. * Moves {@code number} child records from {@code oldLoc} to {@code newLoc}.
  87. * @param oldLoc the current location of the records to move
  88. * @param newLoc the new location for the records
  89. * @param number the number of records to move
  90. */
  91. private void moveChildRecords(int oldLoc, int newLoc, int number) {
  92. if(oldLoc == newLoc) { return; }
  93. if(number == 0) { return; }
  94. // Check that we're not asked to move too many
  95. if(oldLoc+number > _children.length) {
  96. throw new IllegalArgumentException("Asked to move more records than there are!");
  97. }
  98. // Do the move
  99. ArrayUtil.arrayMoveWithin(_children, oldLoc, newLoc, number);
  100. }
  101. /**
  102. * Finds the first child record of the given type,
  103. * or null if none of the child records are of the
  104. * given type. Does not descend.
  105. */
  106. public org.apache.poi.hslf.record.Record findFirstOfType(long type) {
  107. for (org.apache.poi.hslf.record.Record r : _children) {
  108. if (r.getRecordType() == type) {
  109. return r;
  110. }
  111. }
  112. return null;
  113. }
  114. /**
  115. * Remove a child record from this record container
  116. *
  117. * @param ch the child to remove
  118. * @return the removed record
  119. */
  120. public org.apache.poi.hslf.record.Record removeChild(Record ch) {
  121. org.apache.poi.hslf.record.Record rm = null;
  122. ArrayList<org.apache.poi.hslf.record.Record> lst = new ArrayList<>();
  123. for(org.apache.poi.hslf.record.Record r : _children) {
  124. if(r != ch) {
  125. lst.add(r);
  126. } else {
  127. rm = r;
  128. }
  129. }
  130. _children = lst.toArray(new org.apache.poi.hslf.record.Record[0]);
  131. return rm;
  132. }
  133. /* ===============================================================
  134. * External Move Methods
  135. * ===============================================================
  136. */
  137. /**
  138. * Add a new child record onto a record's list of children.
  139. *
  140. * @param newChild the child record to be added
  141. * @return the position of the added child within the list, i.e. the last index
  142. */
  143. public int appendChildRecord(Record newChild) {
  144. return appendChild(newChild);
  145. }
  146. /**
  147. * Adds the given Child Record after the supplied record
  148. * @param newChild The record to add as new child.
  149. * @param after The record after which the given record should be added.
  150. * @return the position of the added child within the list
  151. */
  152. public int addChildAfter(Record newChild, Record after) {
  153. // Decide where we're going to put it
  154. int loc = findChildLocation(after);
  155. if(loc == -1) {
  156. throw new IllegalArgumentException("Asked to add a new child after another record, but that record wasn't one of our children!");
  157. }
  158. // Add one place after the supplied record
  159. addChildAt(newChild, loc+1);
  160. return loc+1;
  161. }
  162. /**
  163. * Adds the given Child Record before the supplied record
  164. * @param newChild The record to add as new child.
  165. * @param before The record before which the given record should be added.
  166. * @return the position of the added child within the list
  167. */
  168. public int addChildBefore(Record newChild, Record before) {
  169. // Decide where we're going to put it
  170. int loc = findChildLocation(before);
  171. if(loc == -1) {
  172. throw new IllegalArgumentException("Asked to add a new child before another record, but that record wasn't one of our children!");
  173. }
  174. // Add at the place of the supplied record
  175. addChildAt(newChild, loc);
  176. return loc;
  177. }
  178. /**
  179. * Set child records.
  180. *
  181. * @param records the new child records
  182. */
  183. public void setChildRecord(org.apache.poi.hslf.record.Record[] records) {
  184. this._children = records.clone();
  185. }
  186. /* ===============================================================
  187. * External Serialisation Methods
  188. * ===============================================================
  189. */
  190. /**
  191. * Write out our header, and our children.
  192. * @param headerA the first byte of the header
  193. * @param headerB the second byte of the header
  194. * @param type the record type
  195. * @param children our child records
  196. * @param out the stream to write to
  197. */
  198. public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException {
  199. // Create a UnsynchronizedByteArrayOutputStream to hold everything in
  200. try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
  201. // Write out our header, less the size
  202. baos.write(new byte[]{headerA, headerB});
  203. byte[] typeB = new byte[2];
  204. LittleEndian.putShort(typeB, 0, (short) type);
  205. baos.write(typeB);
  206. baos.write(new byte[]{0, 0, 0, 0});
  207. // Write out our children
  208. for (Record aChildren : children) {
  209. aChildren.writeOut(baos);
  210. }
  211. // Grab the bytes back
  212. byte[] toWrite = baos.toByteArray();
  213. // Update our header with the size
  214. // Don't forget to knock 8 more off, since we don't include the header in the size
  215. LittleEndian.putInt(toWrite, 4, (toWrite.length - 8));
  216. // Write out the bytes
  217. out.write(toWrite);
  218. }
  219. }
  220. /**
  221. * Find the records that are parent-aware, and tell them who their parent is
  222. */
  223. public static void handleParentAwareRecords(RecordContainer br) {
  224. // Loop over child records, looking for interesting ones
  225. for (org.apache.poi.hslf.record.Record record : br.getChildRecords()) {
  226. // Tell parent aware records of their parent
  227. if (record instanceof ParentAwareRecord) {
  228. ((ParentAwareRecord) record).setParentRecord(br);
  229. }
  230. // Walk on down for the case of container records
  231. if (record instanceof RecordContainer) {
  232. handleParentAwareRecords((RecordContainer)record);
  233. }
  234. }
  235. }
  236. @Override
  237. public Map<String, Supplier<?>> getGenericProperties() {
  238. return null;
  239. }
  240. }