Package org.odftoolkit.simple

Source Code of org.odftoolkit.simple.PresentationDocument

/************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
*
* Use is subject to license terms.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0. You can also
* obtain a copy of the License at http://odftoolkit.org/docs/license.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************/
package org.odftoolkit.simple;

import java.awt.Rectangle;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.xpath.XPathConstants;

import org.odftoolkit.odfdom.dom.OdfContentDom;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.attribute.text.TextAnchorTypeAttribute;
import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPageElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPageThumbnailElement;
import org.odftoolkit.odfdom.dom.element.office.OfficePresentationElement;
import org.odftoolkit.odfdom.dom.element.presentation.PresentationNotesElement;
import org.odftoolkit.odfdom.pkg.MediaType;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.odftoolkit.odfdom.pkg.OdfPackage;
import org.odftoolkit.odfdom.type.CellRangeAddressList;
import org.odftoolkit.simple.chart.AbstractChartContainer;
import org.odftoolkit.simple.chart.Chart;
import org.odftoolkit.simple.chart.ChartContainer;
import org.odftoolkit.simple.chart.DataSet;
import org.odftoolkit.simple.presentation.Slide;
import org.odftoolkit.simple.presentation.Notes.NotesBuilder;
import org.odftoolkit.simple.presentation.Slide.SlideBuilder;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* This class represents an empty ODF presentation.
*/
public class PresentationDocument extends Document implements ChartContainer{

  private static final String EMPTY_PRESENTATION_DOCUMENT_PATH = "/OdfPresentationDocument.odp";
  static final Resource EMPTY_PRESENTATION_DOCUMENT_RESOURCE = new Resource(EMPTY_PRESENTATION_DOCUMENT_PATH);
  private final SlideBuilder slideBuilder;
  private final NotesBuilder notesBuilder;
  private ChartContainerImpl chartContainerImpl;
 
  /**
   * It represents the defined values of presentation:class. The
   * presentation:class attribute classifies presentation shapes by their
   * usage within a draw page.
   *
   * @since 0.5
   */
  public static enum PresentationClass {
    /**
     * presentation charts are standard object shapes.
     */
    CHAT("chart"),
    /**
     * presentation graphics are standard graphic shapes.
     */
    GRAPHIC("graphic"),
    /**
     * presentation handouts are placeholder for the drawing page in a
     * handout page.
     */
    HANDOUT("handout"),
    /**
     * presentation notes are used on notes pages.
     */
    NOTES("notes"),
    /**
     * presentation objects are standard object shapes.
     */
    OBJECTS("object"),
    /**
     * presentation organization charts are standard object shapes.
     */
    ORGCHART("orgchart"),
    /**
     * outlines are standard text shapes
     */
    OUTLINE("outline"),
    /**
     * presentation pages are used on notes pages
     */
    PAGE("page"),
    /**
     * subtitles are standard text shapes
     */
    SUBTITLE("subtitle"),
    /**
     * presentation tables are standard object shapes
     */
    TABLE("table"),
    /**
     * presentation texts are standard text shapes
     */
    TEXT("text"),
    /**
     * titles are standard text shapes
     */
    TITLE("title"),
    /**
     * drawing shape is used as a date and/or time shape. Date and Time
     * shapes are standard text shapes.
     */
    DATETIME("date-time"),
    /**
     * drawing shape is used as a footer. Footer shapes are standard text
     * shapes.
     */
    FOOTER("footer"),
    /**
     * drawing shape is used as a header. Header shapes are standard text
     * shapes.
     */
    HEADER("header"),
    /**
     * drawing shape is used as a page number shape. Page Number shapes are
     * standard text shapes.
     */
    PAGENUMBER("page-number");

    private String value;

    PresentationClass(String aClass) {
      value = aClass;
    }

    @Override
    public String toString() {
      return value;
    }

    public static PresentationClass enumValueOf(String aValue) {
      if ((aValue == null) || (aValue.length() == 0))
        return null;

      for (PresentationClass aIter : values()) {
        if (aValue.equals(aIter.toString())) {
          return aIter;
        }
      }
      throw new RuntimeException("Unsupported Presentation Class!");
    }
  }
 
  /**
   * This enum contains all possible media types of PresentationDocument
   * documents.
   */
  public enum OdfMediaType implements MediaType {

    PRESENTATION(Document.OdfMediaType.PRESENTATION), PRESENTATION_TEMPLATE(
        Document.OdfMediaType.PRESENTATION_TEMPLATE);
    private final Document.OdfMediaType mMediaType;

    OdfMediaType(Document.OdfMediaType mediaType) {
      this.mMediaType = mediaType;
    }

    /**
     * @return the ODF mediatype of this document
     */
    public Document.OdfMediaType getOdfMediaType() {
      return mMediaType;
    }

    /**
     * @return the mediatype of this document
     */
    public String getMediaTypeString() {
      return mMediaType.getMediaTypeString();
    }

    /**
     * @return the ODF filesuffix of this document
     */
    public String getSuffix() {
      return mMediaType.getSuffix();
    }

    /**
     *
     * @param mediaType
     *            string defining an ODF document
     * @return the according OdfMediatype encapuslating the given string and
     *         the suffix
     */
    public static Document.OdfMediaType getOdfMediaType(String mediaType) {
      return Document.OdfMediaType.getOdfMediaType(mediaType);
    }
  }

  /**
   * Creates an empty presentation document.
   *
   * @return ODF presentation document based on a default template
   * @throws java.lang.Exception
   *             - if the document could not be created
   */
  public static PresentationDocument newPresentationDocument() throws Exception {
    return (PresentationDocument) Document.loadTemplate(EMPTY_PRESENTATION_DOCUMENT_RESOURCE,
        Document.OdfMediaType.PRESENTATION);
  }

  /**
   * Creates an empty presentation template.
   *
   * @return ODF presentation template based on a default
   * @throws Exception
   *             - if the template could not be created
   */
  public static PresentationDocument newPresentationTemplateDocument() throws Exception {
    PresentationDocument doc = (PresentationDocument) Document.loadTemplate(EMPTY_PRESENTATION_DOCUMENT_RESOURCE,
        Document.OdfMediaType.PRESENTATION_TEMPLATE);
    doc.changeMode(OdfMediaType.PRESENTATION_TEMPLATE);
    return doc;
  }

  /**
   * To avoid data duplication a new document is only created, if not already
   * opened. A document is cached by this constructor using the internalpath
   * as key.
   */
  protected PresentationDocument(OdfPackage pkg, String internalPath, PresentationDocument.OdfMediaType odfMediaType) {
    super(pkg, internalPath, odfMediaType.mMediaType);
    slideBuilder = new SlideBuilder(this);
    notesBuilder = new NotesBuilder(this);
  }

  /**
   * Creates an PresentationDocument from the OpenDocument provided by a
   * resource Stream.
   *
   * <p>
   * Since an InputStream does not provide the arbitrary (non sequentiell)
   * read access needed by PresentationDocument, the InputStream is cached.
   * This usually takes more time compared to the other createInternalDocument
   * methods. An advantage of caching is that there are no problems
   * overwriting an input file.
   * </p>
   *
   * <p>
   * If the resource stream is not a ODF presentation document,
   * ClassCastException might be thrown.
   * </p>
   *
   * @param inputStream
   *            - the InputStream of the ODF presentation document.
   * @return the presentation document created from the given InputStream
   * @throws java.lang.Exception
   *             - if the document could not be created.
   */
  public static PresentationDocument loadDocument(InputStream inputStream) throws Exception {
    return (PresentationDocument) Document.loadDocument(inputStream);
  }

  /**
   * Loads an PresentationDocument from the provided path.
   *
   * <p>
   * PresentationDocument relies on the file being available for read access
   * over the whole lifecycle of PresentationDocument.
   * </p>
   *
   * <p>
   * If the resource stream is not a ODF presentation document,
   * ClassCastException might be thrown.
   * </p>
   *
   * @param documentPath
   *            - the path from where the document can be loaded
   * @return the presentation document from the given path or NULL if the
   *         media type is not supported by SIMPLE.
   * @throws java.lang.Exception
   *             - if the document could not be created.
   */
  public static PresentationDocument loadDocument(String documentPath) throws Exception {
    return (PresentationDocument) Document.loadDocument(documentPath);
  }

  /**
   * Creates an PresentationDocument from the OpenDocument provided by a File.
   *
   * <p>
   * PresentationDocument relies on the file being available for read access
   * over the whole lifecycle of PresentationDocument.
   * </p>
   *
   * <p>
   * If the resource stream is not a ODF presentation document,
   * ClassCastException might be thrown.
   * </p>
   *
   * @param file
   *            - a file representing the ODF presentation document.
   * @return the presentation document created from the given File
   * @throws java.lang.Exception
   *             - if the document could not be created.
   */
  public static PresentationDocument loadDocument(File file) throws Exception {
    return (PresentationDocument) Document.loadDocument(file);
  }

  /**
   * Get the content root of a presentation document.
   *
   * @return content root, representing the office:presentation tag
   * @throws Exception
   *             if the file DOM could not be created.
   */
  @Override
  public OfficePresentationElement getContentRoot() throws Exception {
    return super.getContentRoot(OfficePresentationElement.class);
  }

  /**
   * Switches this instance to the given type. This method can be used to e.g.
   * convert a document instance to a template and vice versa. Changes take
   * affect in the package when saving the document.
   *
   * @param type
   *            the compatible ODF mediatype.
   */
  public void changeMode(OdfMediaType type) {
    setOdfMediaType(type.mMediaType);
  }

  private boolean hasCheckSlideName = false;

  /**
   * Return the slide builder of this document. Every presentation document
   * has a slide builder.
   *
   * @return the slide builder of this document.
   * @since 0.3.5
   */
  public SlideBuilder getSlideBuilder() {
    return slideBuilder;
  }

  /**
   * Return the notes builder of this document. Every presentation document
   * has a notes builder.
   *
   * @return the notes builder of this document.
   * @since 0.3.5
   */
  public NotesBuilder getNotesBuilder() {
    return notesBuilder;
  }

  /**
   * Return the slide at a specified position in this presentation. Return
   * null if the index is out of range.
   *
   * @param index
   *            the index of the slide to be returned
   * @return a draw slide at the specified position
   */
  public Slide getSlideByIndex(int index) {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return null;
    }
    NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    if ((index >= slideNodes.getLength()) || (index < 0)) {
      return null;
    }
    DrawPageElement slideElement = (DrawPageElement) slideNodes.item(index);
    return Slide.getInstance(slideElement);
  }

  /**
   * Get the number of the slides in this presentation.
   *
   * @return the number of slides
   */
  public int getSlideCount() {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return 0;
    }
    NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    return slideNodes.getLength();
  }

  /**
   * Return the slide which have a specified slide name in this presentation.
   * <p>
   * According to the odf specification "The draw:name attribute specifies a
   * name by which this element can be referenced. It is optional but if
   * present, must be unique within the document instance. If not present, an
   * application may generate a unique name."
   * <p>
   * If the name is null, then return null because all the slide must has its
   * own unique name.
   *
   * @param name
   *            the specified slide name
   * @return the slide whose name equals to the specified name
   */
  public Slide getSlideByName(String name) {
    checkAllSlideName();
    if (name == null) {
      return null;
    }
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return null;
    }
    NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    for (int i = 0; i < slideNodes.getLength(); i++) {
      DrawPageElement slideElement = (DrawPageElement) slideNodes.item(i);
      Slide slide = Slide.getInstance(slideElement);
      String slideName = slide.getSlideName();
      if (slideName.equals(name)) {
        return slide;
      }
    }
    return null;
  }

  // when access slide related method, this function should be called
  private void checkAllSlideName() {
    // check if this function is called or not
    if (hasCheckSlideName) {
      return;
    }
    List<String> slideNameList = new ArrayList<String>();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return;
    }
    NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    for (int i = 0; i < slideNodes.getLength(); i++) {
      DrawPageElement slideElement = (DrawPageElement) slideNodes.item(i);
      String slideName = slideElement.getDrawNameAttribute();
      if ((slideName == null) || slideNameList.contains(slideName)) {
        slideName = "page" + (i + 1) + "-" + makeUniqueName();
        slideElement.setDrawNameAttribute(slideName);
      }
      slideNameList.add(slideName);
    }
    hasCheckSlideName = true;
  }

  /**
   * Return a list iterator containing all slides in this presentation.
   *
   * @return a list iterator containing all slides in this presentation
   */
  public Iterator<Slide> getSlides() {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return null;
    }
    ArrayList<Slide> slideList = new ArrayList<Slide>();
    NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    for (int i = 0; i < slideNodes.getLength(); i++) {
      DrawPageElement slideElement = (DrawPageElement) slideNodes.item(i);
      slideList.add(Slide.getInstance(slideElement));
    }
    return slideList.iterator();
  }

  /**
   * Delete the slide at a specified position in this presentation.
   *
   * @param index
   *            the index of the slide that need to be delete
   *            <p>
   *            Throw IndexOutOfBoundsException if the slide index is out of
   *            the presentation document slide count.
   * @return false if the operation was not successful
   */
  public boolean deleteSlideByIndex(int index) {
    boolean success = true;
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      success = false;
      return success;
    }
    NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    if ((index >= slideNodes.getLength()) || (index < 0)) {
      throw new IndexOutOfBoundsException(
          "the specified Index is out of slide count when call deleteSlideByIndex method.");
    }
    DrawPageElement slideElement = (DrawPageElement) slideNodes.item(index);
    // remove all the content of the current page
    // 1. the reference of the path that contained in this slide is 1, then
    // remove it
    // success &= deleteLinkedRef(slideElement);
    // 2.the reference of the style is 1, then remove it
    // in order to save time, do not delete the style here
    // success &= deleteStyleRef(slideElement);
    // these two methods have been merged into 1 method
    success &= removeElementLinkedResource(slideElement);
    // remove the current page element
    contentRoot.removeChild(slideElement);
    adjustNotePageNumber(index);
    return success;
  }

  /**
   * Delete all the slides with a specified name in this presentation.
   *
   * @param name
   *            the name of the slide that need to be delete
   * @return false if the operation was not successful
   */
  public boolean deleteSlideByName(String name) {
    boolean success = true;
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      success = false;
      return success;
    }
    Slide slide = getSlideByName(name);
    DrawPageElement slideElement = slide.getOdfElement();
    // remove all the content of the current page
    // 1. the reference of the path that contained in this slide is 1, then
    // remove its
    success &= deleteLinkedRef(slideElement);
    // 2.the reference of the style is 1, then remove it
    // in order to save time, do not delete style here
    success &= deleteStyleRef(slideElement);
    // remove the current page element
    contentRoot.removeChild(slideElement);
    adjustNotePageNumber(0);
    return success;
  }

  /**
   * Make a copy of the slide at a specified position to another position in
   * this presentation. The original slide which at the dest index and after
   * the dest index will move after.
   * <p>
   *
   * @param source
   *            the source position of the slide need to be copied
   * @param dest
   *            the destination position of the slide need to be copied
   * @param newName
   *            the new name of the copied slide
   * @return the new slide at the destination position with the specified
   *         name, and it has the same content with the slide at the source
   *         position.
   *         <p>
   *         Throw IndexOutOfBoundsException if the slide index is out of the
   *         presentation document slide count. If copy the slide at the end
   *         of document, destIndex should set the same value with the slide
   *         count.
   */
  public Slide copySlide(int source, int dest, String newName) {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return null;
    }
    NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    int slideCount = slideList.getLength();
    if ((source < 0) || (source >= slideCount) || (dest < 0) || (dest > slideCount)) {
      throw new IndexOutOfBoundsException("the specified Index is out of slide count when call copySlide method.");
    }
    DrawPageElement sourceSlideElement = (DrawPageElement) slideList.item(source);
    DrawPageElement cloneSlideElement = (DrawPageElement) sourceSlideElement.cloneNode(true);
    cloneSlideElement.setDrawNameAttribute(newName);
    if (dest == slideCount) {
      contentRoot.appendChild(cloneSlideElement);
    } else {
      DrawPageElement refSlide = (DrawPageElement) slideList.item(dest);
      contentRoot.insertBefore(cloneSlideElement, refSlide);
    }
    adjustNotePageNumber(Math.min(source, dest));
    // in case that the appended new slide have the same name with the
    // original slide
    hasCheckSlideName = false;
    checkAllSlideName();
    return Slide.getInstance(cloneSlideElement);
  }

  /**
   * Move the slide at a specified position to the destination position.
   *
   * @param source
   *            the current index of the slide that need to be moved
   * @param dest
   *            The index of the destination position before the move action
   *            <p>
   *            Throw IndexOutOfBoundsException if the slide index is out of
   *            the presentation document slide count.
   */
  public void moveSlide(int source, int dest) {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return;
    }
    NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    int slideCount = slideList.getLength();
    if ((source < 0) || (source >= slideCount) || (dest < 0) || (dest > slideCount)) {
      throw new IndexOutOfBoundsException("the specified Index is out of slide count when call moveSlide method.");
    }
    DrawPageElement sourceSlide = (DrawPageElement) slideList.item(source);
    if (dest == slideCount) {
      contentRoot.appendChild(sourceSlide);
    } else {
      DrawPageElement refSlide = (DrawPageElement) slideList.item(dest);
      contentRoot.insertBefore(sourceSlide, refSlide);
    }
    adjustNotePageNumber(Math.min(source, dest));
  }

  private Node cloneForeignElement_(Node element, OdfFileDom dom, boolean deep) {
    checkAllSlideName();
    return cloneForeignElement(element, dom, deep);
  }

  /**
   * Append all the slides of the specified presentation document to the
   * current document.
   *
   * @param srcDoc
   *            the specified <code>PresentationDocument</code> that need to
   *            be appended
   */
  public void appendPresentation(PresentationDocument srcDoc) {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    OdfFileDom contentDom = null;
    OfficePresentationElement srcContentRoot = null;
    try {
      contentRoot = getContentRoot();
      contentDom = getContentDom();
      srcContentRoot = srcDoc.getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
    }
    NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    int slideNum = slideList.getLength();
    // clone the srcContentRoot, and make a modification on this clone node.
    OfficePresentationElement srcCloneContentRoot = (OfficePresentationElement) srcContentRoot.cloneNode(true);
    // copy all the referred xlink:href here
    copyLinkedRefInBatch(srcCloneContentRoot, srcDoc);
    // copy all the referred style definition here
    copyForeignStyleRef(srcCloneContentRoot, srcDoc);
    Node child = srcCloneContentRoot.getFirstChild();
    while (child != null) {
      Node cloneElement = cloneForeignElement_(child, contentDom, true);
      contentRoot.appendChild(cloneElement);
      child = child.getNextSibling();
    }
    adjustNotePageNumber(slideNum - 1);

    // in case that the appended new slide have the same name with the
    // original slide
    hasCheckSlideName = false;
    checkAllSlideName();
  }

  /**
   * Make a copy of slide which locates at the specified position of the
   * source presentation document and insert it to the current presentation
   * document at the new position. The original slide which at the dest index
   * and after the dest index will move after.
   *
   * @param destIndex
   *            the new position of the copied slide in the current document
   * @param srcDoc
   *            the source document of the copied slide
   * @param srcIndex
   *            the slide index of the source document that need to be copied
   * @return the new slide which has the same content with the source slide
   *         <p>
   *         Throw IndexOutOfBoundsException if the slide index is out of the
   *         presentation document slide count If insert the foreign slide at
   *         the end of document, destIndex should set the same value with the
   *         slide count of the current presentation document.
   */
  public Slide copyForeignSlide(int destIndex, PresentationDocument srcDoc, int srcIndex) {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    OdfFileDom contentDom = null;
    try {
      contentRoot = getContentRoot();
      contentDom = getContentDom();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return null;
    }
    NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    int slideCount = slideList.getLength();
    if ((destIndex < 0) || (destIndex > slideCount)) {
      throw new IndexOutOfBoundsException(
          "the specified Index is out of slide count when call copyForeignSlide method.");
    }
    Slide sourceSlide = srcDoc.getSlideByIndex(srcIndex);
    DrawPageElement sourceSlideElement = sourceSlide.getOdfElement();
    // clone the sourceSlideEle, and make a modification on this clone node.
    DrawPageElement sourceCloneSlideElement = (DrawPageElement) sourceSlideElement.cloneNode(true);

    // copy all the referred xlink:href here
    copyLinkedRefInBatch(sourceCloneSlideElement, srcDoc);
    // copy all the referred style definition here
    copyForeignStyleRef(sourceCloneSlideElement, srcDoc);
    // clone the sourceCloneSlideEle, and this cloned element should in the
    // current dom tree
    DrawPageElement cloneSlideElement = (DrawPageElement) cloneForeignElement_(sourceCloneSlideElement, contentDom,
        true);
    if (destIndex == slideCount) {
      contentRoot.appendChild(cloneSlideElement);
    } else {
      DrawPageElement refSlide = (DrawPageElement) slideList.item(destIndex);
      contentRoot.insertBefore(cloneSlideElement, refSlide);
    }
    adjustNotePageNumber(destIndex);
    // in case that the appended new slide have the same name with the
    // original slide
    hasCheckSlideName = false;
    checkAllSlideName();
    return Slide.getInstance(cloneSlideElement);
  }

  /**
   * New a slide at the specified position with the specified name, and use
   * the specified slide template. See <code>OdfDrawPage.SlideLayout</code>.
   * <p>
   * If index is invalid, such as larger than the current document slide
   * number or is negative, then append the new slide at the end of the
   * document.
   * <p>
   * The slide name can be null.
   *
   * @param index
   *            the new slide position
   * @param name
   *            the new slide name
   * @param slideLayout
   *            the new slide template
   * @return the new slide which locate at the specified position with the
   *         specified name and apply the specified slide template. If
   *         slideLayout is null, then use the default slide template which is
   *         a blank slide.
   *         <p>
   *         Throw IndexOutOfBoundsException if index is out of the
   *         presentation document slide count.
   */
  public Slide newSlide(int index, String name, Slide.SlideLayout slideLayout) {
    checkAllSlideName();
    OfficePresentationElement contentRoot = null;
    try {
      contentRoot = getContentRoot();
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
      return null;
    }
    NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
    int slideCount = slideList.getLength();
    if ((index < 0) || (index > slideCount)) {
      throw new IndexOutOfBoundsException("the specified Index is out of slide count when call newSlide method.");
    }
    // if insert page at the beginning of the document,
    // get the next page style as the new page style
    // else get the previous page style as the new page style
    DrawPageElement refStyleSlide = null;
    int refSlideIndex = 0;
    if (index > 0) {
      refSlideIndex = index - 1;
    }
    refStyleSlide = (DrawPageElement) slideList.item(refSlideIndex);
    String masterPageStyleName = "Default";
    String masterName = refStyleSlide.getDrawMasterPageNameAttribute();
    if (masterName != null) {
      masterPageStyleName = masterName;
    }
    DrawPageElement newSlideElement = contentRoot.newDrawPageElement(masterPageStyleName);
    newSlideElement.setDrawNameAttribute(name);
    String drawStyleName = refStyleSlide.getDrawStyleNameAttribute();
    if (drawStyleName != null) {
      newSlideElement.setDrawStyleNameAttribute(drawStyleName);
    }
    String pageLayoutName = refStyleSlide.getPresentationPresentationPageLayoutNameAttribute();
    if (pageLayoutName != null) {
      newSlideElement.setPresentationPresentationPageLayoutNameAttribute(pageLayoutName);
    }
    setSlideLayout(newSlideElement, slideLayout);
    // insert notes page
    NodeList noteNodes = refStyleSlide.getElementsByTagNameNS(OdfDocumentNamespace.PRESENTATION.getUri(), "notes");
    if (noteNodes.getLength() > 0) {
      PresentationNotesElement notePage = (PresentationNotesElement) noteNodes.item(0);
      PresentationNotesElement cloneNotePage = (PresentationNotesElement) notePage.cloneNode(true);
      newSlideElement.appendChild(cloneNotePage);
    }
    if (index < slideCount) {
      DrawPageElement refSlide = (DrawPageElement) slideList.item(index);
      contentRoot.insertBefore(newSlideElement, refSlide);
    }
    adjustNotePageNumber(index);
    // in case that the appended new slide have the same name with the
    // original slide
    hasCheckSlideName = false;
    checkAllSlideName();
    return Slide.getInstance(newSlideElement);
  }
 
  public Chart createChart(String title, DataSet dataset, Rectangle rect) {
    return getChartContainerImpl().createChart(title, dataset, rect);
  }

  public Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,
      boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect) {
    return getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,
        rowAsDataSeries, rect);
  }

  public Chart createChart(String title, String[] labels, String[] legends, double[][] data, Rectangle rect) {
    return getChartContainerImpl().createChart(title, labels, legends, data, rect);
  }

  public void deleteChartById(String chartId) {
    getChartContainerImpl().deleteChartById(chartId);
  }

  public void deleteChartByTitle(String title) {
    getChartContainerImpl().deleteChartByTitle(title);
  }

  public Chart getChartById(String chartId) {
    return getChartContainerImpl().getChartById(chartId);
  }

  public List<Chart> getChartByTitle(String title) {
    return getChartContainerImpl().getChartByTitle(title);
  }

  public int getChartCount() {
    return getChartContainerImpl().getChartCount();
  }
 
  // when insert a slide, the note page for this slide is also inserted.
  // note page refer the slide index in order to show the corresponding slide
  // notes view
  // this function is used to adjust note page referred slide index since
  // startIndex
  // when the slide at startIndex has been delete or insert
  private void adjustNotePageNumber(int startIndex) {
    try {
      OfficePresentationElement contentRoot = getContentRoot();
      NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
      for (int i = startIndex; i < getSlideCount(); i++) {
        DrawPageElement page = (DrawPageElement) slideList.item(i);
        NodeList noteNodes = page.getElementsByTagNameNS(OdfDocumentNamespace.PRESENTATION.getUri(), "notes");
        if (noteNodes.getLength() > 0) {
          PresentationNotesElement notePage = (PresentationNotesElement) noteNodes.item(0);
          NodeList thumbnailList = notePage.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(),
              "page-thumbnail");
          if (thumbnailList.getLength() > 0) {
            DrawPageThumbnailElement thumbnail = (DrawPageThumbnailElement) thumbnailList.item(0);
            thumbnail.setDrawPageNumberAttribute(i + 1);
          }
        }
      }
    } catch (Exception e) {
      Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
    }
  }

  // covered element
  // <presentation:notes>, <draw:page-thumbnail>, <draw:frame>
  // <style:presentation-page-layout>
  private void setSlideLayout(DrawPageElement page, Slide.SlideLayout slideLayout) {
    if (slideLayout == null) {
      slideLayout = Slide.SlideLayout.BLANK;
    }
    slideLayout.apply(page);
  }

  public OdfElement getTableContainerElement() {
    throw new UnsupportedOperationException("Presentation document is not supported to hold table directly.");
  }
 
  private ChartContainerImpl getChartContainerImpl() {
    if (chartContainerImpl == null) {
      chartContainerImpl = new ChartContainerImpl(this);
    }
    return chartContainerImpl;
  }
 
  private class ChartContainerImpl extends AbstractChartContainer {
    PresentationDocument sdoc;

    protected ChartContainerImpl(Document doc) {
      super(doc);
      sdoc = (PresentationDocument) doc;
    }

    protected DrawFrameElement getChartFrame() throws Exception {
      OdfContentDom contentDom2 = sdoc.getContentDom();
      DrawFrameElement drawFrame = contentDom2.newOdfElement(DrawFrameElement.class);
      DrawPageElement lastPage = (DrawPageElement) contentDom2.getXPath().evaluate("//draw:page[last()]",
          contentDom2, XPathConstants.NODE);
      lastPage.appendChild(drawFrame);
      drawFrame.setTextAnchorTypeAttribute(TextAnchorTypeAttribute.Value.PAGE.toString());
      return drawFrame;
    }
  }
}
TOP

Related Classes of org.odftoolkit.simple.PresentationDocument

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.