All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.mpxj.mspdi.XsdDuration Maven / Gradle / Ivy

Go to download

Library that provides facilities to allow project information to be manipulated in Java and .Net. Supports a range of data formats: Microsoft Project Exchange (MPX), Microsoft Project (MPP,MPT), Microsoft Project Data Interchange (MSPDI XML), Microsoft Project Database (MPD), Planner (XML), Primavera (PM XML, XER, and database), Asta Powerproject (PP, MDB), Asta Easyplan (PP), Phoenix Project Manager (PPX), FastTrack Schedule (FTS), and the Standard Data Exchange Format (SDEF).

There is a newer version: 13.8.0
Show newest version
/*
 * file:       XsdDuration.java
 * author:     Jon Iles
 * copyright:  (c) Packwood Software 2002-2003
 * date:       20/02/2003
 */

/*
 * 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.mspdi;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;

import net.sf.mpxj.Duration;

/**
 * This class parses and represents an xsd:duration value.
 */
final class XsdDuration
{
   /**
    * Constructor. Parses the xsd:duration value and extracts the
    * duration data from it.
    *
    * @param duration value formatted as an xsd:duration
    */
   XsdDuration(String duration)
   {
      if (duration != null)
      {
         int length = duration.length();
         if (length > 0)
         {
            // We have come across schedules exported from Synchro which represent
            // zero duration as a plain `0` rather than a well-formed XSD Duration.
            // MS Project reads this, so we'll treat it as a special case.
            if (length == 1 && duration.charAt(0) == '0')
            {
               return;
            }

            if (duration.charAt(0) != 'P')
            {
               if (length < 2 || (duration.charAt(0) != '-' && duration.charAt(1) != 'P'))
               {
                  throw new IllegalArgumentException(duration);
               }
            }

            //
            // A minus sign at the start of the XSD duration is the correct way
            // to represent a negative duration according to the spec. MSPDI
            // disagrees and simply uses a negative number for the relevant
            // component of the duration. This code ensures we can parse a
            // spec-compliant value.
            //
            int index;
            boolean negative;
            if (duration.charAt(0) == '-')
            {
               index = 2;
               negative = true;
            }
            else
            {
               index = 1;
               negative = false;
            }

            while (index < length)
            {
               index = readComponent(duration, index, length);
            }

            if (negative == true)
            {
               m_years = -m_years;
               m_months = -m_months;
               m_days = -m_days;
               m_hours = -m_hours;
               m_minutes = -m_minutes;
               m_seconds = -m_seconds;
            }
         }
      }
   }

   /**
    * This constructor allows an xsd:duration to be created from
    * an MPX duration.
    *
    * @param duration An MPX duration.
    */
   XsdDuration(Duration duration)
   {
      if (duration != null)
      {
         double amount = duration.getDuration();

         if (amount != 0)
         {
            switch (duration.getUnits())
            {
               case MINUTES:
               case ELAPSED_MINUTES:
               {
                  m_minutes = (int) amount;
                  m_seconds = (amount * 60) - (m_minutes * 60);
                  break;
               }

               case HOURS:
               case ELAPSED_HOURS:
               {
                  m_hours = (int) amount;
                  amount = (amount * 60) - (m_hours * 60);
                  m_minutes = (int) amount;
                  m_seconds = (amount * 60) - (m_minutes * 60);
                  break;
               }

               case DAYS:
               case ELAPSED_DAYS:
               {
                  m_days = (int) amount;
                  amount = (amount * 24) - (m_days * 24);
                  m_hours = (int) amount;
                  amount = (amount * 60) - (m_hours * 60);
                  m_minutes = (int) amount;
                  m_seconds = (amount * 60) - (m_minutes * 60);
                  break;
               }

               case WEEKS:
               case ELAPSED_WEEKS:
               {
                  amount *= 7;
                  m_days = (int) amount;
                  amount = (amount * 24) - (m_days * 24);
                  m_hours = (int) amount;
                  amount = (amount * 60) - (m_hours * 60);
                  m_minutes = (int) amount;
                  m_seconds = (amount * 60) - (m_minutes * 60);
                  break;
               }

               case MONTHS:
               case ELAPSED_MONTHS:
               {
                  m_months = (int) amount;
                  amount = (amount * 28) - (m_months * 28);
                  m_days = (int) amount;
                  amount = (amount * 24) - (m_days * 24);
                  m_hours = (int) amount;
                  amount = (amount * 60) - (m_hours * 60);
                  m_minutes = (int) amount;
                  m_seconds = (amount * 60) - (m_minutes * 60);
                  break;
               }

               case YEARS:
               case ELAPSED_YEARS:
               {
                  m_years = (int) amount;
                  amount = (amount * 12) - (m_years * 12);
                  m_months = (int) amount;
                  amount = (amount * 28) - (m_months * 28);
                  m_days = (int) amount;
                  amount = (amount * 24) - (m_days * 24);
                  m_hours = (int) amount;
                  amount = (amount * 60) - (m_hours * 60);
                  m_minutes = (int) amount;
                  m_seconds = (amount * 60) - (m_minutes * 60);
                  break;
               }

               default:
               {
                  break;
               }
            }
         }
      }
   }

   /**
    * This method is called repeatedly to parse each duration component
    * from sorting data in xsd:duration format. Each component consists
    * of a number, followed by a letter representing the type.
    *
    * @param duration xsd:duration formatted string
    * @param index current position in the string
    * @param length length of string
    * @return current position in the string
    */
   private int readComponent(String duration, int index, int length)
   {
      char c = 0;
      StringBuilder number = new StringBuilder();

      while (index < length)
      {
         c = duration.charAt(index);

         //
         // We shouldn't see a minus sign here according to the spec,
         // but this is how MSPDI represents negative duration
         // values, so we ensure that we can handle this format.
         //
         if (Character.isDigit(c) || c == '.' || c == '-')
         {
            number.append(c);
         }
         else
         {
            break;
         }

         ++index;
      }

      switch (c)
      {
         case 'Y':
         {
            m_years = Integer.parseInt(number.toString());
            break;
         }

         case 'M':
         {
            if (m_hasTime == false)
            {
               m_months = Integer.parseInt(number.toString());
            }
            else
            {
               m_minutes = Integer.parseInt(number.toString());
            }
            break;
         }

         case 'D':
         {
            m_days = Integer.parseInt(number.toString());
            break;
         }

         case 'T':
         {
            m_hasTime = true;
            break;
         }

         case 'H':
         {
            m_hours = Integer.parseInt(number.toString());
            break;
         }

         case 'S':
         {
            m_seconds = Double.parseDouble(number.toString());
            break;
         }

         default:
         {
            throw new IllegalArgumentException(duration);
         }
      }

      ++index;

      return (index);
   }

   /**
    * Retrieves the number of days.
    *
    * @return int
    */
   public int getDays()
   {
      return (m_days);
   }

   /**
    * Retrieves the number of hours.
    *
    * @return int
    */
   public int getHours()
   {
      return (m_hours);
   }

   /**
    * Retrieves the number of minutes.
    *
    * @return int
    */
   public int getMinutes()
   {
      return (m_minutes);
   }

   /**
    * Retrieves the number of months.
    *
    * @return int
    */
   public int getMonths()
   {
      return (m_months);
   }

   /**
    * Retrieves the number of seconds.
    *
    * @return double
    */
   public double getSeconds()
   {
      return (m_seconds);
   }

   /**
    * Retrieves the number of years.
    *
    * @return int
    */
   public int getYears()
   {
      return (m_years);
   }

   /**
    * Prints the duration.
    *
    * @param microsoftProjectCompatible false for spec compliant, true for readable by Microsoft Project
    * @return XSD duration value
    */
   public String print(boolean microsoftProjectCompatible)
   {
      StringBuilder buffer = new StringBuilder("P");
      boolean negative = false;

      if (m_years != 0 || m_months != 0 || m_days != 0)
      {
         if (m_years < 0)
         {
            negative = true;
            buffer.append(-m_years);
         }
         else
         {
            buffer.append(m_years);
         }
         buffer.append("Y");

         if (m_months < 0)
         {
            negative = true;
            buffer.append(-m_months);
         }
         else
         {
            buffer.append(m_months);
         }
         buffer.append("M");

         if (m_days < 0)
         {
            negative = true;
            buffer.append(-m_days);
         }
         else
         {
            buffer.append(m_days);
         }
         buffer.append("D");
      }

      buffer.append("T");

      if (m_hours < 0)
      {
         negative = true;
         buffer.append(-m_hours);
      }
      else
      {
         buffer.append(m_hours);
      }
      buffer.append("H");

      if (m_minutes < 0)
      {
         negative = true;
         buffer.append(-m_minutes);
      }
      else
      {
         buffer.append(m_minutes);
      }
      buffer.append("M");

      if (m_seconds < 0)
      {
         negative = true;
         buffer.append(FORMAT.format(-m_seconds));
      }
      else
      {
         buffer.append(FORMAT.format(m_seconds));
      }
      buffer.append("S");

      if (negative == true)
      {
         if (microsoftProjectCompatible)
         {
            int index = 0;
            while (index < buffer.length())
            {
               char c = buffer.charAt(index);
               if (Character.isDigit(c) && c != '0')
               {
                  buffer.insert(index, '-');
                  break;
               }
               ++index;
            }
         }
         else
         {
            buffer.insert(0, '-');
         }
      }

      return buffer.toString();
   }

   /**
    * This method generates the string representation of an xsd:duration value.
    *
    * @return xsd:duration value
    */
   @Override public String toString()
   {
      return print(true);
   }

   private boolean m_hasTime;
   private int m_years;
   private int m_months;
   private int m_days;
   private int m_hours;
   private int m_minutes;
   private double m_seconds;

   /**
    * Configure the decimal separator to be independent of the
    * one used by the default locale.
    */
   private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols();
   static
   {
      SYMBOLS.setDecimalSeparator('.');
   }

   private static final DecimalFormat FORMAT = new DecimalFormat("#", SYMBOLS);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy