joniles / mpxj

Primary repository for MPXJ library

Home Page:http://www.mpxj.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Probe file type before reading it

JanecekPetr opened this issue · comments

I have a XML file and I'd like to understand whether it's P6-based or MSP-based or Phoenix-based or something else ... before parsing.

I know I can get that information from projectFile.getProjectProperties().getFileApplication(), but that's too late. I'd like the information before reading the file fully. I found ProjectReaderUtility, but that's too rudimentary, it simply returns an MSPDIReader for XML files which feels clearly wrong.

UniversalProjectReader does what I'd like to know, it fingerprints the content and chooses the correct reader accordingly. Would you be willing to enhance ProjectReaderUtility to fingerprint ambiguous files like XML? Or perhaps this warrants a new public API?

Hi Petr, I'd be happy to provide some kind of "preview" method to UniversalProjectReader to determine what type of project will be read. Leave it with me and I'll have a look at some options.

That was an interesting journey!

I've now refactored UniversalProjectReader to provide a couple of new getProjectReaderProxy methods. The original read and readAll methods are still there, but internally they make use of getProjectReaderProxy.

The getProjectReaderProxy methods return instances of a class implementing the UniversalProjectReader.ProjectReaderProxy interface which encapsulates the state of the reader and the source data just before we start reading the schedule data. This allows you to do two things. The first is your use-case: you can call the getProjectReader method on the proxy, and make decisions based on the reader type.

The second thing this will do is it will allow me to deprecate UniversalProjectReader.setProperties. This was a fairly brittle solution to allow you to pass options to the reader class selected by UniversalProjectReader in order to customise its behaviour. Now you have direct access to the reader instance set up by UniversalProjectReader, so you can configure things as you wish directly on the reader instance before reading starts.

Here's some sample code illustrating both these use cases:

   try (UniversalProjectReader.ProjectReaderProxy proxy = new UniversalProjectReader().getProjectReaderProxy(file))
   {
      ProjectReader reader = proxy.getProjectReader();
      if (reader instanceof SDEFReader)
      {
         // Ignore SDEF files
         return;
      }

      if (reader instanceof PhoenixReader)
      {
         // Configure the Phoenix reader
         ((PhoenixReader)reader).setUseActivityCodesForTaskHierarchy(true);
      }

      ProjectFile project = proxy.read();

      // ...
   }

Things to note: the proxy you get back from getProjectReaderProxy implements AutoCloseable. You must ensure that close is called to release resources held by UniversalProjectReader (we're using try with resources to achieve this in the example code).

If you want to use the proxy to actually read schedule data as the example code above shows, you need to call the proxy's read or readAll methods - UniversalProjectReader may need to have dug deep into the file or stream you have supplied in order to find the schedule data - that state is captured by the proxy. Calling the read or readAll method on the reader instance itself may not work!

The changes are here, and will be merged soon once I had a chance to go over the changes again with a fresh pair of eyes, and have updated the relevant documentation. Assuming everything is ok, I'm planning to create a new release once these changes are merged.

Let me know what you think.

This is a lot more than what I hoped for! I like both of the new capabilities, the properties workaround was indeed iffy and potentially fragile. Thank you. This looks good.

This is a lot more than what I hoped for!

I aim to please! 😉

The changes have been merged. I will start putting together a release now.

The new version with these changes is now available.