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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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.ByteArrayOutputStream;
  17. import java.io.IOException;
  18. import java.io.OutputStream;
  19. import java.util.ArrayList;
  20. import java.util.Map;
  21. import java.util.function.Supplier;
  22. import org.apache.poi.util.ArrayUtil;
  23. import org.apache.poi.util.LittleEndian;
  24. import org.apache.poi.util.Removal;
  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 = new org.apache.poi.hslf.record.Record[(_children.length + 1)];
  67. System.arraycopy(_children, 0, nc, 0, _children.length);
  68. // Switch the arrays
  69. nc[_children.length] = newChild;
  70. _children = nc;
  71. return _children.length;
  72. }
  73. /**
  74. * Adds the given new Child Record at the given location,
  75. * shuffling everything from there on down by one
  76. *
  77. * @param newChild The record to be added as child-record.
  78. * @param position The index where the child should be added, 0-based
  79. */
  80. private void addChildAt(Record newChild, int position) {
  81. // Firstly, have the child added in at the end
  82. appendChild(newChild);
  83. // Now, have them moved to the right place
  84. moveChildRecords( (_children.length-1), position, 1 );
  85. }
  86. /**
  87. * Moves {@code number} child records from {@code oldLoc} to {@code newLoc}.
  88. * @param oldLoc the current location of the records to move
  89. * @param newLoc the new location for the records
  90. * @param number the number of records to move
  91. */
  92. private void moveChildRecords(int oldLoc, int newLoc, int number) {
  93. if(oldLoc == newLoc) { return; }
  94. if(number == 0) { return; }
  95. // Check that we're not asked to move too many
  96. if(oldLoc+number > _children.length) {
  97. throw new IllegalArgumentException("Asked to move more records than there are!");
  98. }
  99. // Do the move
  100. ArrayUtil.arrayMoveWithin(_children, oldLoc, newLoc, number);
  101. }
  102. /**
  103. * Finds the first child record of the given type,
  104. * or null if none of the child records are of the
  105. * given type. Does not descend.
  106. */
  107. public org.apache.poi.hslf.record.Record findFirstOfType(long type) {
  108. for (org.apache.poi.hslf.record.Record r : _children) {
  109. if (r.getRecordType() == type) {
  110. return r;
  111. }
  112. }
  113. return null;
  114. }
  115. /**
  116. * Remove a child record from this record container
  117. *
  118. * @param ch the child to remove
  119. * @return the removed record
  120. */
  121. public org.apache.poi.hslf.record.Record removeChild(Record ch) {
  122. org.apache.poi.hslf.record.Record rm = null;
  123. ArrayList<org.apache.poi.hslf.record.Record> lst = new ArrayList<>();
  124. for(org.apache.poi.hslf.record.Record r : _children) {
  125. if(r != ch) {
  126. lst.add(r);
  127. } else {
  128. rm = r;
  129. }
  130. }
  131. _children = lst.toArray(new org.apache.poi.hslf.record.Record[0]);
  132. return rm;
  133. }
  134. /* ===============================================================
  135. * External Move Methods
  136. * ===============================================================
  137. */
  138. /**
  139. * Add a new child record onto a record's list of children.
  140. *
  141. * @param newChild the child record to be added
  142. * @return the position of the added child within the list, i.e. the last index
  143. */
  144. public int appendChildRecord(Record newChild) {
  145. return appendChild(newChild);
  146. }
  147. /**
  148. * Adds the given Child Record after the supplied record
  149. * @param newChild The record to add as new child.
  150. * @param after The record after which the given record should be added.
  151. * @return the position of the added child within the list
  152. */
  153. public int addChildAfter(Record newChild, Record after) {
  154. // Decide where we're going to put it
  155. int loc = findChildLocation(after);
  156. if(loc == -1) {
  157. throw new IllegalArgumentException("Asked to add a new child after another record, but that record wasn't one of our children!");
  158. }
  159. // Add one place after the supplied record
  160. addChildAt(newChild, loc+1);
  161. return loc+1;
  162. }
  163. /**
  164. * Adds the given Child Record before the supplied record
  165. * @param newChild The record to add as new child.
  166. * @param before The record before which the given record should be added.
  167. * @return the position of the added child within the list
  168. */
  169. public int addChildBefore(Record newChild, Record before) {
  170. // Decide where we're going to put it
  171. int loc = findChildLocation(before);
  172. if(loc == -1) {
  173. throw new IllegalArgumentException("Asked to add a new child before another record, but that record wasn't one of our children!");
  174. }
  175. // Add at the place of the supplied record
  176. addChildAt(newChild, loc);
  177. return loc;
  178. }
  179. /**
  180. * Moves the given Child Record to before the supplied record
  181. *
  182. * @deprecated method is not used within POI and will be removed
  183. */
  184. @Removal(version="3.19")
  185. @Deprecated
  186. public void moveChildBefore(Record child, Record before) {
  187. moveChildrenBefore(child, 1, before);
  188. }
  189. /**
  190. * Moves the given Child Records to before the supplied record
  191. *
  192. * @deprecated method is not used within POI and will be removed
  193. */
  194. @Removal(version="3.19")
  195. @Deprecated
  196. public void moveChildrenBefore(Record firstChild, int number, Record before) {
  197. if(number < 1) { return; }
  198. // Decide where we're going to put them
  199. int newLoc = findChildLocation(before);
  200. if(newLoc == -1) {
  201. throw new IllegalArgumentException("Asked to move children before another record, but that record wasn't one of our children!");
  202. }
  203. // Figure out where they are now
  204. int oldLoc = findChildLocation(firstChild);
  205. if(oldLoc == -1) {
  206. throw new IllegalArgumentException("Asked to move a record that wasn't a child!");
  207. }
  208. // Actually move
  209. moveChildRecords(oldLoc, newLoc, number);
  210. }
  211. /**
  212. * Moves the given Child Records to after the supplied record
  213. *
  214. * @param firstChild the first child to be moved
  215. * @param number the number of records to move
  216. * @param after the record after that the children are moved
  217. *
  218. * @deprecated method is not used within POI and will be removed
  219. */
  220. @Removal(version="3.19")
  221. @Deprecated
  222. public void moveChildrenAfter(Record firstChild, int number, Record after) {
  223. if(number < 1) { return; }
  224. // Decide where we're going to put them
  225. int newLoc = findChildLocation(after);
  226. if(newLoc == -1) {
  227. throw new IllegalArgumentException("Asked to move children before another record, but that record wasn't one of our children!");
  228. }
  229. // We actually want after this though
  230. newLoc++;
  231. // Figure out where they are now
  232. int oldLoc = findChildLocation(firstChild);
  233. if(oldLoc == -1) {
  234. throw new IllegalArgumentException("Asked to move a record that wasn't a child!");
  235. }
  236. // Actually move
  237. moveChildRecords(oldLoc, newLoc, number);
  238. }
  239. /**
  240. * Set child records.
  241. *
  242. * @param records the new child records
  243. */
  244. public void setChildRecord(org.apache.poi.hslf.record.Record[] records) {
  245. this._children = records.clone();
  246. }
  247. /* ===============================================================
  248. * External Serialisation Methods
  249. * ===============================================================
  250. */
  251. /**
  252. * Write out our header, and our children.
  253. * @param headerA the first byte of the header
  254. * @param headerB the second byte of the header
  255. * @param type the record type
  256. * @param children our child records
  257. * @param out the stream to write to
  258. */
  259. public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException {
  260. // Create a ByteArrayOutputStream to hold everything in
  261. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  262. // Write out our header, less the size
  263. baos.write(new byte[] {headerA,headerB});
  264. byte[] typeB = new byte[2];
  265. LittleEndian.putShort(typeB,0,(short)type);
  266. baos.write(typeB);
  267. baos.write(new byte[] {0,0,0,0});
  268. // Write out our children
  269. for (Record aChildren : children) {
  270. aChildren.writeOut(baos);
  271. }
  272. // Grab the bytes back
  273. byte[] toWrite = baos.toByteArray();
  274. // Update our header with the size
  275. // Don't forget to knock 8 more off, since we don't include the
  276. // header in the size
  277. LittleEndian.putInt(toWrite,4,(toWrite.length-8));
  278. // Write out the bytes
  279. out.write(toWrite);
  280. }
  281. /**
  282. * Find the records that are parent-aware, and tell them who their parent is
  283. */
  284. public static void handleParentAwareRecords(RecordContainer br) {
  285. // Loop over child records, looking for interesting ones
  286. for (org.apache.poi.hslf.record.Record record : br.getChildRecords()) {
  287. // Tell parent aware records of their parent
  288. if (record instanceof ParentAwareRecord) {
  289. ((ParentAwareRecord) record).setParentRecord(br);
  290. }
  291. // Walk on down for the case of container records
  292. if (record instanceof RecordContainer) {
  293. handleParentAwareRecords((RecordContainer)record);
  294. }
  295. }
  296. }
  297. @Override
  298. public Map<String, Supplier<?>> getGenericProperties() {
  299. return null;
  300. }
  301. }