net.sf.mpxj.mpp.MPPReader.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.mpxj.mpp.MPPReader.java

Source

/*
 * file:       MPPReader.java
 * author:     Jon Iles
 * copyright:  (c) Packwood Software 2005
 * date:       2005-12-21
 */

/*
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

package net.sf.mpxj.mpp;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import net.sf.mpxj.DateRange;
import net.sf.mpxj.MPXJException;
import net.sf.mpxj.ProjectConfig;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.Relation;
import net.sf.mpxj.Task;
import net.sf.mpxj.listener.ProjectListener;
import net.sf.mpxj.reader.AbstractProjectReader;

import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

/**
 * This class creates a new ProjectFile instance by reading an MPP file.
 */
public final class MPPReader extends AbstractProjectReader {
    /**
     * {@inheritDoc}
     */
    @Override
    public void addProjectListener(ProjectListener listener) {
        if (m_projectListeners == null) {
            m_projectListeners = new LinkedList<ProjectListener>();
        }
        m_projectListeners.add(listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ProjectFile read(InputStream is) throws MPXJException {

        try {

            //
            // Open the file system
            //
            POIFSFileSystem fs = new POIFSFileSystem(is);

            return read(fs);

        } catch (IOException ex) {

            throw new MPXJException(MPXJException.READ_ERROR, ex);

        }
    }

    /**
     * Alternative entry point allowing an MPP file to be read from
     * a user-supplied POI file stream. 
     * 
     * @param fs POI file stream
     * @return ProjectFile instance
     * @throws MPXJException
     */
    public ProjectFile read(POIFSFileSystem fs) throws MPXJException {

        try {
            ProjectFile projectFile = new ProjectFile();
            ProjectConfig config = projectFile.getProjectConfig();

            config.setAutoTaskID(false);
            config.setAutoTaskUniqueID(false);
            config.setAutoResourceID(false);
            config.setAutoResourceUniqueID(false);
            config.setAutoOutlineLevel(false);
            config.setAutoOutlineNumber(false);
            config.setAutoWBS(false);
            config.setAutoCalendarUniqueID(false);
            config.setAutoAssignmentUniqueID(false);

            projectFile.getEventManager().addProjectListeners(m_projectListeners);

            //
            // Open the file system and retrieve the root directory
            //
            DirectoryEntry root = fs.getRoot();

            //
            // Retrieve the CompObj data, validate the file format and process
            //
            CompObj compObj = new CompObj(new DocumentInputStream((DocumentEntry) root.getEntry("\1CompObj")));
            projectFile.getProjectProperties().setFullApplicationName(compObj.getApplicationName());
            projectFile.getProjectProperties().setApplicationVersion(compObj.getApplicationVersion());
            String format = compObj.getFileFormat();
            Class<? extends MPPVariantReader> readerClass = FILE_CLASS_MAP.get(format);
            if (readerClass == null) {
                throw new MPXJException(MPXJException.INVALID_FILE + ": " + format);
            }
            MPPVariantReader reader = readerClass.newInstance();
            reader.process(this, projectFile, root);

            //
            // Update the internal structure. We'll take this opportunity to
            // generate outline numbers for the tasks as they don't appear to
            // be present in the MPP file.
            //
            config.setAutoOutlineNumber(true);
            projectFile.updateStructure();
            config.setAutoOutlineNumber(false);

            //
            // Perform post-processing to set the summary flag and clean
            // up any instances where a task has an empty splits list.
            //
            for (Task task : projectFile.getAllTasks()) {
                task.setSummary(task.getChildTasks().size() != 0);
                List<DateRange> splits = task.getSplits();
                if (splits != null && splits.isEmpty()) {
                    task.setSplits(null);
                }
                validationRelations(task);
            }

            //
            // Ensure that the unique ID counters are correct
            //
            config.updateUniqueCounters();

            return (projectFile);
        }

        catch (IOException ex) {
            throw new MPXJException(MPXJException.READ_ERROR, ex);
        }

        catch (IllegalAccessException ex) {
            throw new MPXJException(MPXJException.READ_ERROR, ex);
        }

        catch (InstantiationException ex) {
            throw new MPXJException(MPXJException.READ_ERROR, ex);
        }
    }

    /**
     * This method validates all relationships for a task, removing
     * any which have been incorrectly read from the MPP file and 
     * point to a parent task.
     * 
     * @param task task under test
     */
    private void validationRelations(Task task) {
        List<Relation> predecessors = task.getPredecessors();
        if (predecessors != null) {
            ArrayList<Relation> invalid = new ArrayList<Relation>();
            for (Relation relation : predecessors) {
                Task sourceTask = relation.getSourceTask();
                Task targetTask = relation.getTargetTask();

                String sourceOutlineNumber = sourceTask.getOutlineNumber();
                String targetOutlineNumber = targetTask.getOutlineNumber();

                if (sourceOutlineNumber != null && targetOutlineNumber != null
                        && sourceOutlineNumber.startsWith(targetOutlineNumber + '.')) {
                    invalid.add(relation);
                }
            }

            for (Relation relation : invalid) {
                relation.getSourceTask().removePredecessor(relation.getTargetTask(), relation.getType(),
                        relation.getLag());
            }
        }
    }

    /**
     * This method retrieves the state of the preserve note formatting flag.
     *
     * @return boolean flag
     */
    public boolean getPreserveNoteFormatting() {
        return (m_preserveNoteFormatting);
    }

    /**
     * This method sets a flag to indicate whether the RTF formatting associated
     * with notes should be preserved or removed. By default the formatting
     * is removed.
     *
     * @param preserveNoteFormatting boolean flag
     */
    public void setPreserveNoteFormatting(boolean preserveNoteFormatting) {
        m_preserveNoteFormatting = preserveNoteFormatting;
    }

    /**
     * If this flag is true, raw timephased data will be retrieved
     * from MS Project: no normalisation will take place.
     * 
     * @return boolean flag
     */
    public boolean getUseRawTimephasedData() {
        return m_useRawTimephasedData;
    }

    /**
     * If this flag is true, raw timephased data will be retrieved
     * from MS Project: no normalisation will take place. 
     * 
     * @param useRawTimephasedData boolean flag
     */
    public void setUseRawTimephasedData(boolean useRawTimephasedData) {
        m_useRawTimephasedData = useRawTimephasedData;
    }

    /**
     * Retrieves a flag which indicates whether presentation data will
     * be read from the MPP file. Not reading this data saves time and memory. 
     * 
     * @return presentation data flag
     */
    public boolean getReadPresentationData() {
        return m_readPresentationData;
    }

    /**
     * Flag to allow time and memory to be saved by not reading
     * presentation data from the MPP file. 
     * 
     * @param readPresentationData set to false to prevent presentation data being read
     */
    public void setReadPresentationData(boolean readPresentationData) {
        m_readPresentationData = readPresentationData;
    }

    /**
     * Flag to determine if the reader should only read the project properties.
     * This allows for rapid access to the document properties, without the
     * cost of reading the entire contents of the project file.
     *    
     * @return true if the reader should only read the project properties 
     */
    public boolean getReadPropertiesOnly() {
        return m_readPropertiesOnly;
    }

    /**
     * Flag to determine if the reader should only read the project properties.
     * This allows for rapid access to the document properties, without the
     * cost of reading the entire contents of the project file.
     * 
     * @param readPropertiesOnly true if the reader should only read the project properties
     */
    public void setReadPropertiesOnly(boolean readPropertiesOnly) {
        m_readPropertiesOnly = readPropertiesOnly;
    }

    /**
     * Set the read password for this Project file. This is needed in order to
     * be allowed to read a read-protected Project file.
     * 
     * Note: Set this each time before calling the read method.
     * 
     * @param password password text
     */
    public void setReadPassword(String password) {
        m_readPassword = password;
    }

    /**
     * Internal only. Get the read password for this Project file. This is 
     * needed in order to be allowed to read a read-protected Project file.
     * 
     * @return password password text
     */
    public String getReadPassword() {
        return m_readPassword;
    }

    /**
     * Set the write password for this Project file. Currently not used.
     * 
     * Note: Set this each time before calling the read method.
     *
     * @param password password text
     */
    public void setWritePassword(String password) {
        m_writePassword = password;
    }

    /**
     * Internal only. Get the write password for this Project file. 
     * Currently not used.
     * 
     * @return password
     */
    public String getWritePassword() {
        return m_writePassword;
    }

    /**
     * Flag used to indicate whether RTF formatting in notes should
     * be preserved. The default value for this flag is false.
     */
    private boolean m_preserveNoteFormatting;

    /**
     * Setting this flag to true allows raw timephased data to be retrieved. 
     */
    private boolean m_useRawTimephasedData;

    /**
     * Flag to allow time and memory to be saved by not reading
     * presentation data from the MPP file.
     */
    private boolean m_readPresentationData = true;
    private boolean m_readPropertiesOnly;

    private String m_readPassword;
    private String m_writePassword;
    private List<ProjectListener> m_projectListeners;

    /**
     * Populate a map of file types and file processing classes.
     */
    private static final Map<String, Class<? extends MPPVariantReader>> FILE_CLASS_MAP = new HashMap<String, Class<? extends MPPVariantReader>>();
    static {
        FILE_CLASS_MAP.put("MSProject.MPP9", MPP9Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPT9", MPP9Reader.class);
        FILE_CLASS_MAP.put("MSProject.GLOBAL9", MPP9Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPP8", MPP8Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPT8", MPP8Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPP12", MPP12Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPT12", MPP12Reader.class);
        FILE_CLASS_MAP.put("MSProject.GLOBAL12", MPP12Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPP14", MPP14Reader.class);
        FILE_CLASS_MAP.put("MSProject.MPT14", MPP14Reader.class);
        FILE_CLASS_MAP.put("MSProject.GLOBAL14", MPP14Reader.class);
    }
}