PageRenderTime 95ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/pptx/src/org/apache/poi/openxml4j/opc/OPCPackage.java

https://github.com/minstrelsy/POI-Android
Java | 1488 lines | 702 code | 136 blank | 650 comment | 140 complexity | 47fc882bc196bd395e952d6ada656cd9 MD5 | raw file
  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.openxml4j.opc;
  16. import java.io.ByteArrayOutputStream;
  17. import java.io.Closeable;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileNotFoundException;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.OutputStream;
  25. import java.net.URI;
  26. import java.net.URISyntaxException;
  27. import java.util.ArrayList;
  28. import java.util.Date;
  29. import java.util.Hashtable;
  30. import java.util.List;
  31. import java.util.concurrent.locks.ReentrantReadWriteLock;
  32. import java.util.regex.Matcher;
  33. import java.util.regex.Pattern;
  34. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  35. import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
  36. import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
  37. import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
  38. import org.apache.poi.openxml4j.exceptions.PartAlreadyExistsException;
  39. import org.apache.poi.openxml4j.opc.internal.ContentType;
  40. import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
  41. import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
  42. import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
  43. import org.apache.poi.openxml4j.opc.internal.PartUnmarshaller;
  44. import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
  45. import org.apache.poi.openxml4j.opc.internal.marshallers.DefaultMarshaller;
  46. import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPackagePropertiesMarshaller;
  47. import org.apache.poi.openxml4j.opc.internal.unmarshallers.PackagePropertiesUnmarshaller;
  48. import org.apache.poi.openxml4j.opc.internal.unmarshallers.UnmarshallContext;
  49. import org.apache.poi.openxml4j.util.Nullable;
  50. import org.apache.poi.util.POILogger;
  51. import org.apache.poi.util.POILogFactory;
  52. /**
  53. * Represents a container that can store multiple data objects.
  54. *
  55. * @author Julien Chable, CDubet
  56. * @version 0.1
  57. */
  58. public abstract class OPCPackage implements RelationshipSource, Closeable {
  59. /**
  60. * Logger.
  61. */
  62. private static POILogger logger = POILogFactory.getLogger(OPCPackage.class);
  63. /**
  64. * Default package access.
  65. */
  66. protected static final PackageAccess defaultPackageAccess = PackageAccess.READ_WRITE;
  67. /**
  68. * Package access.
  69. */
  70. private PackageAccess packageAccess;
  71. /**
  72. * Package parts collection.
  73. */
  74. protected PackagePartCollection partList;
  75. /**
  76. * Package relationships.
  77. */
  78. protected PackageRelationshipCollection relationships;
  79. /**
  80. * Part marshallers by content type.
  81. */
  82. protected Hashtable<ContentType, PartMarshaller> partMarshallers;
  83. /**
  84. * Default part marshaller.
  85. */
  86. protected PartMarshaller defaultPartMarshaller;
  87. /**
  88. * Part unmarshallers by content type.
  89. */
  90. protected Hashtable<ContentType, PartUnmarshaller> partUnmarshallers;
  91. /**
  92. * Core package properties.
  93. */
  94. protected PackagePropertiesPart packageProperties;
  95. /**
  96. * Manage parts content types of this package.
  97. */
  98. protected ContentTypeManager contentTypeManager;
  99. /**
  100. * Flag if a modification is done to the document.
  101. */
  102. protected boolean isDirty = false;
  103. /**
  104. * File path of this package.
  105. */
  106. protected String originalPackagePath;
  107. /**
  108. * Output stream for writing this package.
  109. */
  110. protected OutputStream output;
  111. /**
  112. * Constructor.
  113. *
  114. * @param access
  115. * Package access.
  116. */
  117. OPCPackage(PackageAccess access) {
  118. if (getClass() != ZipPackage.class) {
  119. throw new IllegalArgumentException("PackageBase may not be subclassed");
  120. }
  121. init();
  122. this.packageAccess = access;
  123. }
  124. /**
  125. * Initialize the package instance.
  126. */
  127. private void init() {
  128. this.partMarshallers = new Hashtable<ContentType, PartMarshaller>(5);
  129. this.partUnmarshallers = new Hashtable<ContentType, PartUnmarshaller>(2);
  130. try {
  131. // Add 'default' unmarshaller
  132. this.partUnmarshallers.put(new ContentType(
  133. ContentTypes.CORE_PROPERTIES_PART),
  134. new PackagePropertiesUnmarshaller());
  135. // Add default marshaller
  136. this.defaultPartMarshaller = new DefaultMarshaller();
  137. // TODO Delocalize specialized marshallers
  138. this.partMarshallers.put(new ContentType(
  139. ContentTypes.CORE_PROPERTIES_PART),
  140. new ZipPackagePropertiesMarshaller());
  141. } catch (InvalidFormatException e) {
  142. // Should never happen
  143. throw new OpenXML4JRuntimeException(
  144. "Package.init() : this exception should never happen, " +
  145. "if you read this message please send a mail to the developers team. : " +
  146. e.getMessage()
  147. );
  148. }
  149. }
  150. /**
  151. * Open a package with read/write permission.
  152. *
  153. * @param path
  154. * The document path.
  155. * @return A Package object, else <b>null</b>.
  156. * @throws InvalidFormatException
  157. * If the specified file doesn't exist, and a parsing error
  158. * occur.
  159. */
  160. public static OPCPackage open(String path) throws InvalidFormatException {
  161. return open(path, defaultPackageAccess);
  162. }
  163. /**
  164. * Open a package.
  165. *
  166. * @param path
  167. * The document path.
  168. * @param access
  169. * PackageBase access.
  170. * @return A PackageBase object, else <b>null</b>.
  171. * @throws InvalidFormatException
  172. * If the specified file doesn't exist, and a parsing error
  173. * occur.
  174. */
  175. public static OPCPackage open(String path, PackageAccess access)
  176. throws InvalidFormatException {
  177. if (path == null || "".equals(path.trim())
  178. || (new File(path).exists() && new File(path).isDirectory()))
  179. throw new IllegalArgumentException("path");
  180. OPCPackage pack = new ZipPackage(path, access);
  181. if (pack.partList == null && access != PackageAccess.WRITE) {
  182. pack.getParts();
  183. }
  184. pack.originalPackagePath = new File(path).getAbsolutePath();
  185. return pack;
  186. }
  187. /**
  188. * Open a package.
  189. *
  190. * Note - uses quite a bit more memory than {@link #open(String)}, which
  191. * doesn't need to hold the whole zip file in memory, and can take advantage
  192. * of native methods
  193. *
  194. * @param in
  195. * The InputStream to read the package from
  196. * @return A PackageBase object
  197. */
  198. public static OPCPackage open(InputStream in) throws InvalidFormatException,
  199. IOException {
  200. OPCPackage pack = new ZipPackage(in, PackageAccess.READ_WRITE);
  201. if (pack.partList == null) {
  202. pack.getParts();
  203. }
  204. return pack;
  205. }
  206. /**
  207. * Opens a package if it exists, else it creates one.
  208. *
  209. * @param file
  210. * The file to open or to create.
  211. * @return A newly created package if the specified file does not exist,
  212. * else the package extract from the file.
  213. * @throws InvalidFormatException
  214. * Throws if the specified file exist and is not valid.
  215. */
  216. public static OPCPackage openOrCreate(File file) throws InvalidFormatException {
  217. OPCPackage retPackage = null;
  218. if (file.exists()) {
  219. retPackage = open(file.getAbsolutePath());
  220. } else {
  221. retPackage = create(file);
  222. }
  223. return retPackage;
  224. }
  225. /**
  226. * Creates a new package.
  227. *
  228. * @param path
  229. * Path of the document.
  230. * @return A newly created PackageBase ready to use.
  231. */
  232. public static OPCPackage create(String path) {
  233. return create(new File(path));
  234. }
  235. /**
  236. * Creates a new package.
  237. *
  238. * @param file
  239. * Path of the document.
  240. * @return A newly created PackageBase ready to use.
  241. */
  242. public static OPCPackage create(File file) {
  243. if (file == null || (file.exists() && file.isDirectory()))
  244. throw new IllegalArgumentException("file");
  245. if (file.exists()) {
  246. throw new InvalidOperationException(
  247. "This package (or file) already exists : use the open() method or delete the file.");
  248. }
  249. // Creates a new package
  250. OPCPackage pkg = null;
  251. pkg = new ZipPackage();
  252. pkg.originalPackagePath = file.getAbsolutePath();
  253. configurePackage(pkg);
  254. return pkg;
  255. }
  256. public static OPCPackage create(OutputStream output) {
  257. OPCPackage pkg = null;
  258. pkg = new ZipPackage();
  259. pkg.originalPackagePath = null;
  260. pkg.output = output;
  261. configurePackage(pkg);
  262. return pkg;
  263. }
  264. /**
  265. * Configure the package.
  266. *
  267. * @param pkg
  268. */
  269. private static void configurePackage(OPCPackage pkg) {
  270. try {
  271. // Content type manager
  272. pkg.contentTypeManager = new ZipContentTypeManager(null, pkg);
  273. // Add default content types for .xml and .rels
  274. pkg.contentTypeManager
  275. .addContentType(
  276. PackagingURIHelper
  277. .createPartName(PackagingURIHelper.PACKAGE_RELATIONSHIPS_ROOT_URI),
  278. ContentTypes.RELATIONSHIPS_PART);
  279. pkg.contentTypeManager
  280. .addContentType(PackagingURIHelper
  281. .createPartName("/default.xml"),
  282. ContentTypes.PLAIN_OLD_XML);
  283. // Init some PackageBase properties
  284. pkg.packageProperties = new PackagePropertiesPart(pkg,
  285. PackagingURIHelper.CORE_PROPERTIES_PART_NAME);
  286. pkg.packageProperties.setCreatorProperty("Generated by OpenXML4J");
  287. pkg.packageProperties.setCreatedProperty(new Nullable<Date>(
  288. new Date()));
  289. } catch (InvalidFormatException e) {
  290. // Should never happen
  291. throw new IllegalStateException(e);
  292. }
  293. }
  294. /**
  295. * Flush the package : save all.
  296. *
  297. * @see #close()
  298. */
  299. public void flush() {
  300. throwExceptionIfReadOnly();
  301. if (this.packageProperties != null) {
  302. this.packageProperties.flush();
  303. }
  304. this.flushImpl();
  305. }
  306. /**
  307. * Close the open, writable package and save its content.
  308. *
  309. * If your package is open read only, then you should call {@link #revert()}
  310. * when finished with the package.
  311. *
  312. * @throws IOException
  313. * If an IO exception occur during the saving process.
  314. */
  315. public void close() throws IOException {
  316. if (this.packageAccess == PackageAccess.READ) {
  317. logger.log(POILogger.WARN,
  318. "The close() method is intended to SAVE a package. This package is open in READ ONLY mode, use the revert() method instead !");
  319. revert();
  320. return;
  321. }
  322. // Save the content
  323. ReentrantReadWriteLock l = new ReentrantReadWriteLock();
  324. try {
  325. l.writeLock().lock();
  326. if (this.originalPackagePath != null
  327. && !"".equals(this.originalPackagePath.trim())) {
  328. File targetFile = new File(this.originalPackagePath);
  329. if (!targetFile.exists()
  330. || !(this.originalPackagePath
  331. .equalsIgnoreCase(targetFile.getAbsolutePath()))) {
  332. // Case of a package created from scratch
  333. save(targetFile);
  334. } else {
  335. closeImpl();
  336. }
  337. } else if (this.output != null) {
  338. save(this.output);
  339. output.close();
  340. }
  341. } finally {
  342. l.writeLock().unlock();
  343. }
  344. // Clear
  345. this.contentTypeManager.clearAll();
  346. }
  347. /**
  348. * Close the package WITHOUT saving its content. Reinitialize this package
  349. * and cancel all changes done to it.
  350. */
  351. public void revert() {
  352. revertImpl();
  353. }
  354. /**
  355. * Add a thumbnail to the package. This method is provided to make easier
  356. * the addition of a thumbnail in a package. You can do the same work by
  357. * using the traditionnal relationship and part mechanism.
  358. *
  359. * @param path
  360. * The full path to the image file.
  361. */
  362. public void addThumbnail(String path) throws IOException {
  363. // Check parameter
  364. if ("".equals(path))
  365. throw new IllegalArgumentException("path");
  366. // Get the filename from the path
  367. String filename = path
  368. .substring(path.lastIndexOf(File.separatorChar) + 1);
  369. // Create the thumbnail part name
  370. String contentType = ContentTypes
  371. .getContentTypeFromFileExtension(filename);
  372. PackagePartName thumbnailPartName = null;
  373. try {
  374. thumbnailPartName = PackagingURIHelper.createPartName("/docProps/"
  375. + filename);
  376. } catch (InvalidFormatException e) {
  377. try {
  378. thumbnailPartName = PackagingURIHelper
  379. .createPartName("/docProps/thumbnail"
  380. + path.substring(path.lastIndexOf(".") + 1));
  381. } catch (InvalidFormatException e2) {
  382. throw new InvalidOperationException(
  383. "Can't add a thumbnail file named '" + filename + "'");
  384. }
  385. }
  386. // Check if part already exist
  387. if (this.getPart(thumbnailPartName) != null)
  388. throw new InvalidOperationException(
  389. "You already add a thumbnail named '" + filename + "'");
  390. // Add the thumbnail part to this package.
  391. PackagePart thumbnailPart = this.createPart(thumbnailPartName,
  392. contentType, false);
  393. // Add the relationship between the package and the thumbnail part
  394. this.addRelationship(thumbnailPartName, TargetMode.INTERNAL,
  395. PackageRelationshipTypes.THUMBNAIL);
  396. // Copy file data to the newly created part
  397. FileInputStream is = new FileInputStream(path);
  398. StreamHelper.copyStream(is, thumbnailPart
  399. .getOutputStream());
  400. is.close();
  401. }
  402. /**
  403. * Throws an exception if the package access mode is in read only mode
  404. * (PackageAccess.Read).
  405. *
  406. * @throws InvalidOperationException
  407. * Throws if a writing operation is done on a read only package.
  408. * @see org.apache.poi.openxml4j.opc.PackageAccess
  409. */
  410. void throwExceptionIfReadOnly() throws InvalidOperationException {
  411. if (packageAccess == PackageAccess.READ)
  412. throw new InvalidOperationException(
  413. "Operation not allowed, document open in read only mode!");
  414. }
  415. /**
  416. * Throws an exception if the package access mode is in write only mode
  417. * (PackageAccess.Write). This method is call when other methods need write
  418. * right.
  419. *
  420. * @throws InvalidOperationException
  421. * Throws if a read operation is done on a write only package.
  422. * @see org.apache.poi.openxml4j.opc.PackageAccess
  423. */
  424. void throwExceptionIfWriteOnly() throws InvalidOperationException {
  425. if (packageAccess == PackageAccess.WRITE)
  426. throw new InvalidOperationException(
  427. "Operation not allowed, document open in write only mode!");
  428. }
  429. /**
  430. * Retrieves or creates if none exists, core package property part.
  431. *
  432. * @return The PackageProperties part of this package.
  433. */
  434. public PackageProperties getPackageProperties()
  435. throws InvalidFormatException {
  436. this.throwExceptionIfWriteOnly();
  437. // If no properties part has been found then we create one
  438. if (this.packageProperties == null) {
  439. this.packageProperties = new PackagePropertiesPart(this,
  440. PackagingURIHelper.CORE_PROPERTIES_PART_NAME);
  441. }
  442. return this.packageProperties;
  443. }
  444. /**
  445. * Retrieve a part identified by its name.
  446. *
  447. * @param partName
  448. * Part name of the part to retrieve.
  449. * @return The part with the specified name, else <code>null</code>.
  450. */
  451. public PackagePart getPart(PackagePartName partName) {
  452. throwExceptionIfWriteOnly();
  453. if (partName == null)
  454. throw new IllegalArgumentException("partName");
  455. // If the partlist is null, then we parse the package.
  456. if (partList == null) {
  457. try {
  458. getParts();
  459. } catch (InvalidFormatException e) {
  460. return null;
  461. }
  462. }
  463. return getPartImpl(partName);
  464. }
  465. /**
  466. * Retrieve parts by content type.
  467. *
  468. * @param contentType
  469. * The content type criteria.
  470. * @return All part associated to the specified content type.
  471. */
  472. public ArrayList<PackagePart> getPartsByContentType(String contentType) {
  473. ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();
  474. for (PackagePart part : partList.values()) {
  475. if (part.getContentType().equals(contentType))
  476. retArr.add(part);
  477. }
  478. return retArr;
  479. }
  480. /**
  481. * Retrieve parts by relationship type.
  482. *
  483. * @param relationshipType
  484. * Relationship type.
  485. * @return All parts which are the target of a relationship with the
  486. * specified type, if the method can't retrieve relationships from
  487. * the package, then return <code>null</code>.
  488. */
  489. public ArrayList<PackagePart> getPartsByRelationshipType(
  490. String relationshipType) {
  491. if (relationshipType == null)
  492. throw new IllegalArgumentException("relationshipType");
  493. ArrayList<PackagePart> retArr = new ArrayList<PackagePart>();
  494. for (PackageRelationship rel : getRelationshipsByType(relationshipType)) {
  495. retArr.add(getPart(rel));
  496. }
  497. return retArr;
  498. }
  499. public List<PackagePart> getPartsByName(final Pattern namePattern) {
  500. if (namePattern == null) {
  501. throw new IllegalArgumentException("name pattern must not be null");
  502. }
  503. ArrayList<PackagePart> result = new ArrayList<PackagePart>();
  504. for (PackagePart part : partList.values()) {
  505. PackagePartName partName = part.getPartName();
  506. String name = partName.getName();
  507. Matcher matcher = namePattern.matcher(name);
  508. if (matcher.matches()) {
  509. result.add(part);
  510. }
  511. }
  512. return result;
  513. }
  514. /**
  515. * Get the target part from the specified relationship.
  516. *
  517. * @param partRel
  518. * The part relationship uses to retrieve the part.
  519. */
  520. public PackagePart getPart(PackageRelationship partRel) {
  521. PackagePart retPart = null;
  522. ensureRelationships();
  523. for (PackageRelationship rel : relationships) {
  524. if (rel.getRelationshipType().equals(partRel.getRelationshipType())) {
  525. try {
  526. retPart = getPart(PackagingURIHelper.createPartName(rel
  527. .getTargetURI()));
  528. } catch (InvalidFormatException e) {
  529. continue;
  530. }
  531. break;
  532. }
  533. }
  534. return retPart;
  535. }
  536. /**
  537. * Load the parts of the archive if it has not been done yet. The
  538. * relationships of each part are not loaded.
  539. *
  540. * Note - Rule M4.1 states that there may only ever be one Core
  541. * Properties Part, but Office produced files will sometimes
  542. * have multiple! As Office ignores all but the first, we relax
  543. * Compliance with Rule M4.1, and ignore all others silently too.
  544. *
  545. * @return All this package's parts.
  546. */
  547. public ArrayList<PackagePart> getParts() throws InvalidFormatException {
  548. throwExceptionIfWriteOnly();
  549. // If the part list is null, we parse the package to retrieve all parts.
  550. if (partList == null) {
  551. /* Variables use to validate OPC Compliance */
  552. // Check rule M4.1 -> A format consumer shall consider more than
  553. // one core properties relationship for a package to be an error
  554. // (We just log it and move on, as real files break this!)
  555. boolean hasCorePropertiesPart = false;
  556. boolean needCorePropertiesPart = true;
  557. PackagePart[] parts = this.getPartsImpl();
  558. this.partList = new PackagePartCollection();
  559. for (PackagePart part : parts) {
  560. if (partList.containsKey(part._partName))
  561. throw new InvalidFormatException(
  562. "A part with the name '" +
  563. part._partName +
  564. "' already exist : Packages shall not contain equivalent " +
  565. "part names and package implementers shall neither create " +
  566. "nor recognize packages with equivalent part names. [M1.12]");
  567. // Check OPC compliance rule M4.1
  568. if (part.getContentType().equals(
  569. ContentTypes.CORE_PROPERTIES_PART)) {
  570. if (!hasCorePropertiesPart) {
  571. hasCorePropertiesPart = true;
  572. } else {
  573. logger.log(POILogger.WARN, "OPC Compliance error [M4.1]: " +
  574. "there is more than one core properties relationship in the package! " +
  575. "POI will use only the first, but other software may reject this file.");
  576. }
  577. }
  578. PartUnmarshaller partUnmarshaller = partUnmarshallers.get(part._contentType);
  579. if (partUnmarshaller != null) {
  580. UnmarshallContext context = new UnmarshallContext(this,
  581. part._partName);
  582. try {
  583. PackagePart unmarshallPart = partUnmarshaller
  584. .unmarshall(context, part.getInputStream());
  585. partList.put(unmarshallPart._partName, unmarshallPart);
  586. // Core properties case-- use first CoreProperties part we come across
  587. // and ignore any subsequent ones
  588. if (unmarshallPart instanceof PackagePropertiesPart &&
  589. hasCorePropertiesPart &&
  590. needCorePropertiesPart) {
  591. this.packageProperties = (PackagePropertiesPart) unmarshallPart;
  592. needCorePropertiesPart = false;
  593. }
  594. } catch (IOException ioe) {
  595. logger.log(POILogger.WARN, "Unmarshall operation : IOException for "
  596. + part._partName);
  597. continue;
  598. } catch (InvalidOperationException invoe) {
  599. throw new InvalidFormatException(invoe.getMessage());
  600. }
  601. } else {
  602. try {
  603. partList.put(part._partName, part);
  604. } catch (InvalidOperationException e) {
  605. throw new InvalidFormatException(e.getMessage());
  606. }
  607. }
  608. }
  609. }
  610. return new ArrayList<PackagePart>(partList.values());
  611. }
  612. /**
  613. * Create and add a part, with the specified name and content type, to the
  614. * package.
  615. *
  616. * @param partName
  617. * Part name.
  618. * @param contentType
  619. * Part content type.
  620. * @return The newly created part.
  621. * @throws InvalidFormatException
  622. * If rule M1.12 is not verified : Packages shall not contain
  623. * equivalent part names and package implementers shall neither
  624. * create nor recognize packages with equivalent part names.
  625. * @see #createPartImpl(PackagePartName, String, boolean)
  626. */
  627. public PackagePart createPart(PackagePartName partName, String contentType) {
  628. return this.createPart(partName, contentType, true);
  629. }
  630. /**
  631. * Create and add a part, with the specified name and content type, to the
  632. * package. For general purpose, prefer the overload version of this method
  633. * without the 'loadRelationships' parameter.
  634. *
  635. * @param partName
  636. * Part name.
  637. * @param contentType
  638. * Part content type.
  639. * @param loadRelationships
  640. * Specify if the existing relationship part, if any, logically
  641. * associated to the newly created part will be loaded.
  642. * @return The newly created part.
  643. * @throws InvalidFormatException
  644. * If rule M1.12 is not verified : Packages shall not contain
  645. * equivalent part names and package implementers shall neither
  646. * create nor recognize packages with equivalent part names.
  647. * @see {@link#createPartImpl(URI, String)}
  648. */
  649. PackagePart createPart(PackagePartName partName, String contentType,
  650. boolean loadRelationships) {
  651. throwExceptionIfReadOnly();
  652. if (partName == null) {
  653. throw new IllegalArgumentException("partName");
  654. }
  655. if (contentType == null || contentType.equals("")) {
  656. throw new IllegalArgumentException("contentType");
  657. }
  658. // Check if the specified part name already exists
  659. if (partList.containsKey(partName)
  660. && !partList.get(partName).isDeleted()) {
  661. throw new PartAlreadyExistsException(
  662. "A part with the name '" + partName.getName() + "'" +
  663. " already exists : Packages shall not contain equivalent part names and package" +
  664. " implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
  665. }
  666. /* Check OPC compliance */
  667. // Rule [M4.1]: The format designer shall specify and the format producer
  668. // shall create at most one core properties relationship for a package.
  669. // A format consumer shall consider more than one core properties
  670. // relationship for a package to be an error. If present, the
  671. // relationship shall target the Core Properties part.
  672. // Note - POI will read files with more than one Core Properties, which
  673. // Office sometimes produces, but is strict on generation
  674. if (contentType.equals(ContentTypes.CORE_PROPERTIES_PART)) {
  675. if (this.packageProperties != null)
  676. throw new InvalidOperationException(
  677. "OPC Compliance error [M4.1]: you try to add more than one core properties relationship in the package !");
  678. }
  679. /* End check OPC compliance */
  680. PackagePart part = this.createPartImpl(partName, contentType,
  681. loadRelationships);
  682. this.contentTypeManager.addContentType(partName, contentType);
  683. this.partList.put(partName, part);
  684. this.isDirty = true;
  685. return part;
  686. }
  687. /**
  688. * Add a part to the package.
  689. *
  690. * @param partName
  691. * Part name of the part to create.
  692. * @param contentType
  693. * type associated with the file
  694. * @param content
  695. * the contents to add. In order to have faster operation in
  696. * document merge, the data are stored in memory not on a hard
  697. * disk
  698. *
  699. * @return The new part.
  700. * @see #createPart(PackagePartName, String)
  701. */
  702. public PackagePart createPart(PackagePartName partName, String contentType,
  703. ByteArrayOutputStream content) {
  704. PackagePart addedPart = this.createPart(partName, contentType);
  705. if (addedPart == null) {
  706. return null;
  707. }
  708. // Extract the zip entry content to put it in the part content
  709. if (content != null) {
  710. try {
  711. OutputStream partOutput = addedPart.getOutputStream();
  712. if (partOutput == null) {
  713. return null;
  714. }
  715. partOutput.write(content.toByteArray(), 0, content.size());
  716. partOutput.close();
  717. } catch (IOException ioe) {
  718. return null;
  719. }
  720. } else {
  721. return null;
  722. }
  723. return addedPart;
  724. }
  725. /**
  726. * Add the specified part to the package. If a part already exists in the
  727. * package with the same name as the one specified, then we replace the old
  728. * part by the specified part.
  729. *
  730. * @param part
  731. * The part to add (or replace).
  732. * @return The part added to the package, the same as the one specified.
  733. * @throws InvalidFormatException
  734. * If rule M1.12 is not verified : Packages shall not contain
  735. * equivalent part names and package implementers shall neither
  736. * create nor recognize packages with equivalent part names.
  737. */
  738. protected PackagePart addPackagePart(PackagePart part) {
  739. throwExceptionIfReadOnly();
  740. if (part == null) {
  741. throw new IllegalArgumentException("part");
  742. }
  743. if (partList.containsKey(part._partName)) {
  744. if (!partList.get(part._partName).isDeleted()) {
  745. throw new InvalidOperationException(
  746. "A part with the name '"
  747. + part._partName.getName()
  748. + "' already exists : Packages shall not contain equivalent part names and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
  749. }
  750. // If the specified partis flagged as deleted, we make it
  751. // available
  752. part.setDeleted(false);
  753. // and delete the old part to replace it thereafeter
  754. this.partList.remove(part._partName);
  755. }
  756. this.partList.put(part._partName, part);
  757. this.isDirty = true;
  758. return part;
  759. }
  760. /**
  761. * Remove the specified part in this package. If this part is relationship
  762. * part, then delete all relationships in the source part.
  763. *
  764. * @param part
  765. * The part to remove. If <code>null</code>, skip the action.
  766. * @see #removePart(PackagePartName)
  767. */
  768. public void removePart(PackagePart part) {
  769. if (part != null) {
  770. removePart(part.getPartName());
  771. }
  772. }
  773. /**
  774. * Remove a part in this package. If this part is relationship part, then
  775. * delete all relationships in the source part.
  776. *
  777. * @param partName
  778. * The part name of the part to remove.
  779. */
  780. public void removePart(PackagePartName partName) {
  781. throwExceptionIfReadOnly();
  782. if (partName == null || !this.containPart(partName))
  783. throw new IllegalArgumentException("partName");
  784. // Delete the specified part from the package.
  785. if (this.partList.containsKey(partName)) {
  786. this.partList.get(partName).setDeleted(true);
  787. this.removePartImpl(partName);
  788. this.partList.remove(partName);
  789. } else {
  790. this.removePartImpl(partName);
  791. }
  792. // Delete content type
  793. this.contentTypeManager.removeContentType(partName);
  794. // If this part is a relationship part, then delete all relationships of
  795. // the source part.
  796. if (partName.isRelationshipPartURI()) {
  797. URI sourceURI = PackagingURIHelper
  798. .getSourcePartUriFromRelationshipPartUri(partName.getURI());
  799. PackagePartName sourcePartName;
  800. try {
  801. sourcePartName = PackagingURIHelper.createPartName(sourceURI);
  802. } catch (InvalidFormatException e) {
  803. logger
  804. .log(POILogger.ERROR, "Part name URI '"
  805. + sourceURI
  806. + "' is not valid ! This message is not intended to be displayed !");
  807. return;
  808. }
  809. if (sourcePartName.getURI().equals(
  810. PackagingURIHelper.PACKAGE_ROOT_URI)) {
  811. clearRelationships();
  812. } else if (containPart(sourcePartName)) {
  813. PackagePart part = getPart(sourcePartName);
  814. if (part != null)
  815. part.clearRelationships();
  816. }
  817. }
  818. this.isDirty = true;
  819. }
  820. /**
  821. * Remove a part from this package as well as its relationship part, if one
  822. * exists, and all parts listed in the relationship part. Be aware that this
  823. * do not delete relationships which target the specified part.
  824. *
  825. * @param partName
  826. * The name of the part to delete.
  827. * @throws InvalidFormatException
  828. * Throws if the associated relationship part of the specified
  829. * part is not valid.
  830. */
  831. public void removePartRecursive(PackagePartName partName)
  832. throws InvalidFormatException {
  833. // Retrieves relationship part, if one exists
  834. PackagePart relPart = this.partList.get(PackagingURIHelper
  835. .getRelationshipPartName(partName));
  836. // Retrieves PackagePart object from the package
  837. PackagePart partToRemove = this.partList.get(partName);
  838. if (relPart != null) {
  839. PackageRelationshipCollection partRels = new PackageRelationshipCollection(
  840. partToRemove);
  841. for (PackageRelationship rel : partRels) {
  842. PackagePartName partNameToRemove = PackagingURIHelper
  843. .createPartName(PackagingURIHelper.resolvePartUri(rel
  844. .getSourceURI(), rel.getTargetURI()));
  845. removePart(partNameToRemove);
  846. }
  847. // Finally delete its relationship part if one exists
  848. this.removePart(relPart._partName);
  849. }
  850. // Delete the specified part
  851. this.removePart(partToRemove._partName);
  852. }
  853. /**
  854. * Delete the part with the specified name and its associated relationships
  855. * part if one exists. Prefer the use of this method to delete a part in the
  856. * package, compare to the remove() methods that don't remove associated
  857. * relationships part.
  858. *
  859. * @param partName
  860. * Name of the part to delete
  861. */
  862. public void deletePart(PackagePartName partName) {
  863. if (partName == null)
  864. throw new IllegalArgumentException("partName");
  865. // Remove the part
  866. this.removePart(partName);
  867. // Remove the relationships part
  868. this.removePart(PackagingURIHelper.getRelationshipPartName(partName));
  869. }
  870. /**
  871. * Delete the part with the specified name and all part listed in its
  872. * associated relationships part if one exists. This process is recursively
  873. * apply to all parts in the relationships part of the specified part.
  874. * Prefer the use of this method to delete a part in the package, compare to
  875. * the remove() methods that don't remove associated relationships part.
  876. *
  877. * @param partName
  878. * Name of the part to delete
  879. */
  880. public void deletePartRecursive(PackagePartName partName) {
  881. if (partName == null || !this.containPart(partName))
  882. throw new IllegalArgumentException("partName");
  883. PackagePart partToDelete = this.getPart(partName);
  884. // Remove the part
  885. this.removePart(partName);
  886. // Remove all relationship parts associated
  887. try {
  888. for (PackageRelationship relationship : partToDelete
  889. .getRelationships()) {
  890. PackagePartName targetPartName = PackagingURIHelper
  891. .createPartName(PackagingURIHelper.resolvePartUri(
  892. partName.getURI(), relationship.getTargetURI()));
  893. this.deletePartRecursive(targetPartName);
  894. }
  895. } catch (InvalidFormatException e) {
  896. logger.log(POILogger.WARN, "An exception occurs while deleting part '"
  897. + partName.getName()
  898. + "'. Some parts may remain in the package. - "
  899. + e.getMessage());
  900. return;
  901. }
  902. // Remove the relationships part
  903. PackagePartName relationshipPartName = PackagingURIHelper
  904. .getRelationshipPartName(partName);
  905. if (relationshipPartName != null && containPart(relationshipPartName))
  906. this.removePart(relationshipPartName);
  907. }
  908. /**
  909. * Check if a part already exists in this package from its name.
  910. *
  911. * @param partName
  912. * Part name to check.
  913. * @return <i>true</i> if the part is logically added to this package, else
  914. * <i>false</i>.
  915. */
  916. public boolean containPart(PackagePartName partName) {
  917. return (this.getPart(partName) != null);
  918. }
  919. /**
  920. * Add a relationship to the package (except relationships part).
  921. *
  922. * Check rule M4.1 : The format designer shall specify and the format
  923. * producer shall create at most one core properties relationship for a
  924. * package. A format consumer shall consider more than one core properties
  925. * relationship for a package to be an error. If present, the relationship
  926. * shall target the Core Properties part.
  927. *
  928. * Check rule M1.25: The Relationships part shall not have relationships to
  929. * any other part. Package implementers shall enforce this requirement upon
  930. * the attempt to create such a relationship and shall treat any such
  931. * relationship as invalid.
  932. *
  933. * @param targetPartName
  934. * Target part name.
  935. * @param targetMode
  936. * Target mode, either Internal or External.
  937. * @param relationshipType
  938. * Relationship type.
  939. * @param relID
  940. * ID of the relationship.
  941. * @see PackageRelationshipTypes
  942. */
  943. public PackageRelationship addRelationship(PackagePartName targetPartName,
  944. TargetMode targetMode, String relationshipType, String relID) {
  945. /* Check OPC compliance */
  946. // Check rule M4.1 : The format designer shall specify and the format
  947. // producer
  948. // shall create at most one core properties relationship for a package.
  949. // A format consumer shall consider more than one core properties
  950. // relationship for a package to be an error. If present, the
  951. // relationship shall target the Core Properties part.
  952. if (relationshipType.equals(PackageRelationshipTypes.CORE_PROPERTIES)
  953. && this.packageProperties != null)
  954. throw new InvalidOperationException(
  955. "OPC Compliance error [M4.1]: can't add another core properties part ! Use the built-in package method instead.");
  956. /*
  957. * Check rule M1.25: The Relationships part shall not have relationships
  958. * to any other part. Package implementers shall enforce this
  959. * requirement upon the attempt to create such a relationship and shall
  960. * treat any such relationship as invalid.
  961. */
  962. if (targetPartName.isRelationshipPartURI()) {
  963. throw new InvalidOperationException(
  964. "Rule M1.25: The Relationships part shall not have relationships to any other part.");
  965. }
  966. /* End OPC compliance */
  967. ensureRelationships();
  968. PackageRelationship retRel = relationships.addRelationship(
  969. targetPartName.getURI(), targetMode, relationshipType, relID);
  970. this.isDirty = true;
  971. return retRel;
  972. }
  973. /**
  974. * Add a package relationship.
  975. *
  976. * @param targetPartName
  977. * Target part name.
  978. * @param targetMode
  979. * Target mode, either Internal or External.
  980. * @param relationshipType
  981. * Relationship type.
  982. * @see PackageRelationshipTypes
  983. */
  984. public PackageRelationship addRelationship(PackagePartName targetPartName,
  985. TargetMode targetMode, String relationshipType) {
  986. return this.addRelationship(targetPartName, targetMode,
  987. relationshipType, null);
  988. }
  989. /**
  990. * Adds an external relationship to a part (except relationships part).
  991. *
  992. * The targets of external relationships are not subject to the same
  993. * validity checks that internal ones are, as the contents is potentially
  994. * any file, URL or similar.
  995. *
  996. * @param target
  997. * External target of the relationship
  998. * @param relationshipType
  999. * Type of relationship.
  1000. * @return The newly created and added relationship
  1001. * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
  1002. * java.lang.String)
  1003. */
  1004. public PackageRelationship addExternalRelationship(String target,
  1005. String relationshipType) {
  1006. return addExternalRelationship(target, relationshipType, null);
  1007. }
  1008. /**
  1009. * Adds an external relationship to a part (except relationships part).
  1010. *
  1011. * The targets of external relationships are not subject to the same
  1012. * validity checks that internal ones are, as the contents is potentially
  1013. * any file, URL or similar.
  1014. *
  1015. * @param target
  1016. * External target of the relationship
  1017. * @param relationshipType
  1018. * Type of relationship.
  1019. * @param id
  1020. * Relationship unique id.
  1021. * @return The newly created and added relationship
  1022. * @see org.apache.poi.openxml4j.opc.RelationshipSource#addExternalRelationship(java.lang.String,
  1023. * java.lang.String)
  1024. */
  1025. public PackageRelationship addExternalRelationship(String target,
  1026. String relationshipType, String id) {
  1027. if (target == null) {
  1028. throw new IllegalArgumentException("target");
  1029. }
  1030. if (relationshipType == null) {
  1031. throw new IllegalArgumentException("relationshipType");
  1032. }
  1033. URI targetURI;
  1034. try {
  1035. targetURI = new URI(target);
  1036. } catch (URISyntaxException e) {
  1037. throw new IllegalArgumentException("Invalid target - " + e);
  1038. }
  1039. ensureRelationships();
  1040. PackageRelationship retRel = relationships.addRelationship(targetURI,
  1041. TargetMode.EXTERNAL, relationshipType, id);
  1042. this.isDirty = true;
  1043. return retRel;
  1044. }
  1045. /**
  1046. * Delete a relationship from this package.
  1047. *
  1048. * @param id
  1049. * Id of the relationship to delete.
  1050. */
  1051. public void removeRelationship(String id) {
  1052. if (relationships != null) {
  1053. relationships.removeRelationship(id);
  1054. this.isDirty = true;
  1055. }
  1056. }
  1057. /**
  1058. * Retrieves all package relationships.
  1059. *
  1060. * @return All package relationships of this package.
  1061. * @throws OpenXML4JException
  1062. * @see #getRelationshipsHelper(String)
  1063. */
  1064. public PackageRelationshipCollection getRelationships() {
  1065. return getRelationshipsHelper(null);
  1066. }
  1067. /**
  1068. * Retrieves all relationships with the specified type.
  1069. *
  1070. * @param relationshipType
  1071. * The filter specifying the relationship type.
  1072. * @return All relationships with the specified relationship type.
  1073. */
  1074. public PackageRelationshipCollection getRelationshipsByType(
  1075. String relationshipType) {
  1076. throwExceptionIfWriteOnly();
  1077. if (relationshipType == null) {
  1078. throw new IllegalArgumentException("relationshipType");
  1079. }
  1080. return getRelationshipsHelper(relationshipType);
  1081. }
  1082. /**
  1083. * Retrieves all relationships with specified id (normally just ine because
  1084. * a relationship id is supposed to be unique).
  1085. *
  1086. * @param id
  1087. * Id of the wanted relationship.
  1088. */
  1089. private PackageRelationshipCollection getRelationshipsHelper(String id) {
  1090. throwExceptionIfWriteOnly();
  1091. ensureRelationships();
  1092. return this.relationships.getRelationships(id);
  1093. }
  1094. /**
  1095. * Clear package relationships.
  1096. */
  1097. public void clearRelationships() {
  1098. if (relationships != null) {
  1099. relationships.clear();
  1100. this.isDirty = true;
  1101. }
  1102. }
  1103. /**
  1104. * Ensure that the relationships collection is not null.
  1105. */
  1106. public void ensureRelationships() {
  1107. if (this.relationships == null) {
  1108. try {
  1109. this.relationships = new PackageRelationshipCollection(this);
  1110. } catch (InvalidFormatException e) {
  1111. this.relationships = new PackageRelationshipCollection();
  1112. }
  1113. }
  1114. }
  1115. /**
  1116. * @see org.apache.poi.openxml4j.opc.RelationshipSource#getRelationship(java.lang.String)
  1117. */
  1118. public PackageRelationship getRelationship(String id) {
  1119. return this.relationships.getRelationshipByID(id);
  1120. }
  1121. /**
  1122. * @see org.apache.poi.openxml4j.opc.RelationshipSource#hasRelationships()
  1123. */
  1124. public boolean hasRelationships() {
  1125. return (relationships.size() > 0);
  1126. }
  1127. /**
  1128. * @see org.apache.poi.openxml4j.opc.RelationshipSource#isRelationshipExists(org.apache.poi.openxml4j.opc.PackageRelationship)
  1129. */
  1130. public boolean isRelationshipExists(PackageRelationship rel) {
  1131. for (PackageRelationship r : this.getRelationships()) {
  1132. if (r == rel)
  1133. return true;
  1134. }
  1135. return false;
  1136. }
  1137. /**
  1138. * Add a marshaller.
  1139. *
  1140. * @param contentType
  1141. * The content type to bind to the specified marshaller.
  1142. * @param marshaller
  1143. * The marshaller to register with the specified content type.
  1144. */
  1145. public void addMarshaller(String contentType, PartMarshaller marshaller) {
  1146. try {
  1147. partMarshallers.put(new ContentType(contentType), marshaller);
  1148. } catch (InvalidFormatException e) {
  1149. logger.log(POILogger.WARN, "The specified content type is not valid: '"
  1150. + e.getMessage() + "'. The marshaller will not be added !");
  1151. }
  1152. }
  1153. /**
  1154. * Add an unmarshaller.
  1155. *
  1156. * @param contentType
  1157. * The content type to bind to the specified unmarshaller.
  1158. * @param unmarshaller
  1159. * The unmarshaller to register with the specified content type.
  1160. */
  1161. public void addUnmarshaller(String contentType,
  1162. PartUnmarshaller unmarshaller) {
  1163. try {
  1164. partUnmarshallers.put(new ContentType(contentType), unmarshaller);
  1165. } catch (InvalidFormatException e) {
  1166. logger.log(POILogger.WARN, "The specified content type is not valid: '"
  1167. + e.getMessage()
  1168. + "'. The unmarshaller will not be added !");
  1169. }
  1170. }
  1171. /**
  1172. * Remove a marshaller by its content type.
  1173. *
  1174. * @param contentType
  1175. * The content type associated with the marshaller to remove.
  1176. */
  1177. public void removeMarshaller(String contentType) {
  1178. partMarshallers.remove(contentType);
  1179. }
  1180. /**
  1181. * Remove an unmarshaller by its content type.
  1182. *
  1183. * @param contentType
  1184. * The content type associated with the unmarshaller to remove.
  1185. */
  1186. public void removeUnmarshaller(String contentType) {
  1187. partUnmarshallers.remove(contentType);
  1188. }
  1189. /* Accesseurs */
  1190. /**
  1191. * Get the package access mode.
  1192. *
  1193. * @return the packageAccess The current package access.
  1194. */
  1195. public PackageAccess getPackageAccess() {
  1196. return packageAccess;
  1197. }
  1198. /**
  1199. * Validates the package compliance with the OPC specifications.
  1200. *
  1201. * @return <b>true</b> if the package is valid else <b>false</b>
  1202. */
  1203. public boolean validatePackage(OPCPackage pkg) throws InvalidFormatException {
  1204. throw new InvalidOperationException("Not implemented yet !!!");
  1205. }
  1206. /**
  1207. * Save the document in the specified file.
  1208. *
  1209. * @param targetFile
  1210. * Destination file.
  1211. * @throws IOException
  1212. * Throws if an IO exception occur.
  1213. * @see #save(OutputStream)
  1214. */
  1215. public void save(File targetFile) throws IOException {
  1216. if (targetFile == null)
  1217. throw new IllegalArgumentException("targetFile");
  1218. this.throwExceptionIfReadOnly();
  1219. // You shouldn't save the the same file, do a close instead
  1220. if(targetFile.exists() &&
  1221. targetFile.getAbsolutePath().equals(this.originalPackagePath)) {
  1222. throw new InvalidOperationException(
  1223. "You can't call save(File) to save to the currently open " +
  1224. "file. To save to the current file, please just call close()"
  1225. );
  1226. }
  1227. // Do the save
  1228. FileOutputStream fos = null;
  1229. try {
  1230. fos = new FileOutputStream(targetFile);
  1231. } catch (FileNotFoundException e) {
  1232. throw new IOException(e.getLocalizedMessage());
  1233. }
  1234. this.save(fos);
  1235. fos.close();
  1236. }
  1237. /**
  1238. * Save the document in the specified output stream.
  1239. *
  1240. * @param outputStream
  1241. * The stream to save the package.
  1242. * @see #saveImpl(OutputStream)
  1243. */
  1244. public void save(OutputStream outputStream) throws IOException {
  1245. throwExceptionIfReadOnly();
  1246. this.saveImpl(outputStream);
  1247. }
  1248. /**
  1249. * Core method to create a package part. This method must be implemented by
  1250. * the subclass.
  1251. *
  1252. * @param partName
  1253. * URI of the part to create.
  1254. * @param contentType
  1255. * Content type of the part to create.
  1256. * @return The newly created package part.
  1257. */
  1258. protected abstract PackagePart createPartImpl(PackagePartName partName,
  1259. String contentType, boolean loadRelationships);
  1260. /**
  1261. * Core method to delete a package part. This method must be implemented by
  1262. * the subclass.
  1263. *
  1264. * @param partName
  1265. * The URI of the part to delete.
  1266. */
  1267. protected abstract void removePartImpl(PackagePartName partName);
  1268. /**
  1269. * Flush the package but not save.
  1270. */
  1271. protected abstract void flushImpl();
  1272. /**
  1273. * Close the package and cause a save of the package.
  1274. *
  1275. */
  1276. protected abstract void closeImpl() throws IOException;
  1277. /**
  1278. * Close the package without saving the document. Discard all changes made
  1279. * to this package.
  1280. */
  1281. protected abstract void revertImpl();
  1282. /**
  1283. * Save the package into the specified output stream.
  1284. *
  1285. * @param outputStream
  1286. * The output stream use to save this package.
  1287. */
  1288. protected abstract void saveImpl(OutputStream outputStream)
  1289. throws IOException;
  1290. /**
  1291. * Get the package part mapped to the specified URI.
  1292. *
  1293. * @param partName
  1294. * The URI of the part to retrieve.
  1295. * @return The package part located by the specified URI, else <b>null</b>.
  1296. */
  1297. protected abstract PackagePart getPartImpl(PackagePartName partName);
  1298. /**
  1299. * Get all parts link to the package.
  1300. *
  1301. * @return A list of the part owned by the package.
  1302. */
  1303. protected abstract PackagePart[] getPartsImpl()
  1304. throws InvalidFormatException;
  1305. /**
  1306. * Replace a content type in this package.
  1307. *
  1308. * <p>
  1309. * A typical scneario to call this method is to rename a template file to the main format, e.g.
  1310. * ".dotx" to ".docx"
  1311. * ".dotm" to ".docm"
  1312. * ".xltx" to ".xlsx"
  1313. * ".xltm" to ".xlsm"
  1314. * ".potx" to ".pptx"
  1315. * ".potm" to ".pptm"
  1316. * </p>
  1317. * For example, a code converting a .xlsm macro workbook to .xlsx would look as follows:
  1318. * <p>
  1319. * <pre><code>
  1320. *
  1321. * OPCPackage pkg = OPCPackage.open(new FileInputStream("macro-workbook.xlsm"));
  1322. * pkg.replaceContentType(
  1323. * "application/vnd.ms-excel.sheet.macroEnabled.main+xml",
  1324. * "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml");
  1325. *
  1326. * FileOutputStream out = new FileOutputStream("workbook.xlsx");
  1327. * pkg.save(out);
  1328. * out.close();
  1329. *
  1330. * </code></pre>
  1331. * </p>
  1332. *
  1333. * @param oldContentType the content type to be replaced
  1334. * @param newContentType the replacement
  1335. * @return whether replacement was succesfull
  1336. * @since POI-3.8
  1337. */
  1338. public boolean replaceContentType(String oldContentType, String newContentType){
  1339. boolean success = false;
  1340. ArrayList<PackagePart> list = getPartsByContentType(oldContentType);
  1341. for (PackagePart packagePart : list) {
  1342. if (packagePart.getContentType().equals(oldContentType)) {
  1343. PackagePartName partName = packagePart.getPartName();
  1344. contentTypeManager.addContentType(partName, newContentType);
  1345. success = true;
  1346. }
  1347. }
  1348. return success;
  1349. }
  1350. }